Static cast will also fail if the compiler doesn't know (or is pretending not to know) about the relationship between the types. If your inheritance isn't declared public between the two, the compiler will consider them as unrelated types and give you the same cryptic warning.
This just bit me, so thought I'd share.
Answer from Dragos on Stack OverflowStatic cast will also fail if the compiler doesn't know (or is pretending not to know) about the relationship between the types. If your inheritance isn't declared public between the two, the compiler will consider them as unrelated types and give you the same cryptic warning.
This just bit me, so thought I'd share.
Your reasoning has a very usual flaw; I think we all have made the same mistake sometime. You are thinking of std::vector<> as just an output container because this is how you want to use it now, but it is not.
Just imagine that the following code would compile:
vector<BigObject*>* bigVector = box->ClipObjectInRect(); // OK
ObjList* objVector = static_cast<ObjList*>(bigVector); // Not OK; we'll now see why
objVector->push_back(new SmallObject()); // OUCH
As you can see, allowing that cast would allow you to try to put a SmallObject* in what can only contain BigObject*. This would surely result in a runtime error.
By the way: you can actually cast between arrays of related types. This is a behaviour inherited from C. And it results in runtime errors :)
You should use reinterpret_cast for casting pointers, i.e.
r = reinterpret_cast<int*>(p);
Of course this makes no sense,
unless you want take a int-level look at a double! You'll get some weird output and I don't think this is what you intended. If you want to cast the value pointed to by p to an int then,
*r = static_cast<int>(*p);
Also, r is not allocated so you can do one of the following:
int *r = new int(0);
*r = static_cast<int>(*p);
std::cout << *r << std::endl;
Or
int r = 0;
r = static_cast<int>(*p);
std::cout << r << std::endl;
Aside from being pointers, double* and int* have nothing in common. You could say the same thing for Foo* and Bar* pointer types to any dissimilar structures.
static_cast means that a pointer of the source type can be used as a pointer of the destination type, which requires a subtype relationship.
static_cast - C++ Forum
c++ - Why can't I static_cast between char * and unsigned char *? - Stack Overflow
error: invalid static_cast from type ‘const void*’ to type ‘void (*)()’
c++ - invalid cast from char* to int* - Stack Overflow
They are completely different types see standard:
3.9.1 Fundamental types [basic.fundamental]
1 Objects declared as characters char) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a char object can hold negative values. Characters can be explicitly declared unsigned or
signed. Plain char, signed char, and unsigned char are three distinct types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (basic.types); that is, they have the same object representation. For character types, all bits of the object
representation participate in the value representation. For unsigned character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.
So analogous to this is also why the following fails:
unsigned int* a = new unsigned int(10);
int* b = static_cast<int*>(a); // error different types
a and b are completely different types, really what you are questioning is why is static_cast so restrictive when it can perform the following without problem
unsigned int a = unsigned int(10);
int b = static_cast<int>(a); // OK but may result in loss of precision
and why can it not deduce that the target types are the same bit-field width and can be represented? It can do this for scalar types but for pointers, unless the target is derived from the source and you wish to perform a downcast then casting between pointers is not going to work.
Bjarne Stroustrop states why static_cast's are useful in this link: http://www.stroustrup.com/bs_faq2.html#static-cast but in abbreviated form it is for the user to state clearly what their intentions are and to give the compiler the opportunity to check that what you are intending can be achieved, since static_cast does not support casting between different pointer types then the compiler can catch this error to alert the user and if they really want to do this conversion they then should use reinterpret_cast.
you're trying to convert unrelated pointers with a static_cast. That's not what static_cast is for. Here you can see: Type Casting.
With static_cast you can convert numerical data (e.g. char to unsigned char should work) or pointer to related classes (related by some inheritance). This is both not the case. You want to convert one unrelated pointer to another so you have to use reinterpret_cast.
Basically what you are trying to do is for the compiler the same as trying to convert a char * to a void *.
Ok, here some additional thoughts why allowing this is fundamentally wrong. static_cast can be used to convert numerical types into each other. So it is perfectly legal to write the following:
char x = 5;
unsigned char y = static_cast<unsigned char>(x);
what is also possible:
double d = 1.2;
int i = static_cast<int>(d);
If you look at this code in assembler you'll see that the second cast is not a mere re-interpretation of the bit pattern of d but instead some assembler instructions for conversions are inserted here.
Now if we extend this behavior to arrays, the case where simply a different way of interpreting the bit pattern is sufficient, it might work. But what about casting arrays of doubles to arrays of ints? That's where you either have to declare that you simple want a re-interpretation of the bit patterns - there's a mechanism for that called reinterpret_cast, or you must do some extra work. As you can see simple extending the static_cast for pointer / arrays is not sufficient since it needs to behave similar to static_casting single values of the types. This sometimes needs extra code and it is not clearly definable how this should be done for arrays. In your case - stopping at \0 - because it's the convention? This is not sufficient for non-string cases (number). What will happen if the size of the data-type changes (e.g. int vs. double on x86-32bit)?
The behavior you want can't be properly defined for all use-cases that's why it's not in the C++ standard. Otherwise you would have to remember things like: "i can cast this type to the other as long as they are of type integer, have the same width and ...". This way it's totally clear - either they are related CLASSES - then you can cast the pointers, or they are numerical types - then you can cast the values.
long *plDog = static_cast<long*>(piDog); // invalid type conversionWhy is my static_cast of a pointer failing?
Because it is ill-formed. None of the rules of static_cast apply to the cast that you're attempting. It is an invalid conversion, as you mention in the comments.
A pointer to one object type may not be static casted to a pointer to another object type unless they are pointers to related classes, or when casting to/from pointer to void.
This reference suggests it should be OK: https://en.cppreference.com/w/cpp/language/static_cast
That reference suggests that the conversion you attempt is not OK.
long* plDog = (long*)(piDog); // this is OK too... very weird!!
This is a well-formed conversion. It's not "weird".
There are many conversions that explicit conversions (also called "cast notation" or "C-style cast") allow, and static casts do not allow. This is because static casts have (at least a semblance of) type safety while explicit conversions essentially ask the compiler to pretend that the type system doesn't exist.
Note that indirecting through plDog and accessing the object would result in undefined behaviour. As you can see, it was a good thing that you got the error.
and we aren't allowed to use this one in our coding standards
This is a good limitation to have. This will make it bit harder for your team to write bugs by mistakenly circumventing the type system.
Issue with Visual Studio C++?
No, the issue is that the program is ill-formed. The compiler is correct to inform you about the bug, and is not required to compile the program.
I recommend asking yourself: Why do you want to, or think that you need to do such cast?
TL;DR: If your cast worked, the language would provide no type-safety guarantees for pointers, and is a key part of the motivation for introducing the C++ style casts instead of sticking with the old-school C cast.
In the language of the C/C++ Standard, long* and int* are not "pointer-compatible". You can't implicitly convert a long* to an int* or vice-versa, so static_cast can't do it in a single cast.
The reasoning behind that is
sizeof(long)is not always equal tosizeof(int)for all platforms. They are distinct fundamental types. This is true in general of all distinct C/C++ types even if they have an identical binary layout. They are only "pointer-compatible" if in the syntax of the language you declare the types to be related via implicit conversion.
You can use static_cast when converting from a void* to any pointer to an object type or vice-versa.
Therefore you can do this in two ways:
reinterpret_cast<long*>(piDog);
-or-
static_cast<long*>(static_cast<void*>(piDog));
Stylistically, the use of the reinterpret_cast is a lot clearer. In either case, the validity of the cast is architecture dependent and assumes sizeof(int) == sizeof(long) as well as having the same memory layout.
IOW This is safe for Windows x86 and x64 native, but may not hold for other platforms or CPUs. This is one of the reasons Windows x64 choose to use the LLP64 data model as explained in this blog post.
See cppreference for static_cast and reinterpret_cast
Although an object of a given type can be converted to another type, you can't necessarily cast their pointers.
Converting an X to a Y in this example says "take this X and make me a Y from it". An X is not a Y, but it can be converted to one.
Converting an X* to a Y* says "this is a pointer to X and now I'm saying it's a pointer to Y". That doesn't make sense, because an X is not a Y, so you can't treat it like one until you convert the object.
statc_casting the object works because you create a temporary of the object using the converting constructor.
But when you static_cast a pointer, you're making a temporary copy of the pointer. Pointers don't have converting constructors.
You cannot static_cast unrelated pointers to another. Not even if they have converting constructors.
Even if it were allowed, consider what would happen if it worked like static_casting the object: A temporary would be created using the converting constructor, but instead of assigning the temporary to a variable, you'd be assigning the address of the temporary to a pointer. But since the temporary does not exist after the expression, the pointer would immediately be pointing to invalid memory.
If-statements don't make compile-time decisions; the compiler will always check both branches.
You need to create an overloaded write() function, where the default version streams to cout, and which you can overload for specific types (such as char).
The problem:
Like your compiler said:
error: invalid static_cast from type ‘std::basic_string<char, std::char_traits<char>,
std::allocator<char> >’ to type ‘int16_t’
I suppose your calling your log function like this:
log("my % thing %....", /*my std::string*/ stringToto, 5,/*....*/);
And there is the problem!
When the compiler see you want to static_cast a std::string to a int16_t it generate an error!
What you've done wrong
This part:
if ( std::is_same<T, uint8_t>::value || std::is_same<T, int8_t>::value )
{
int16_t x = value1;
int16_t x = static_cast<int16_t>(value1);
std::cout << x;
}
else
{
std::cout << value1;
}
Why? In fact, even if your condition (std::is_same<T, uint8_t>::value || std::is_same<T, int8_t>::value) is false, your compiler will interpret your static_cast
What should you do
Use a function that get an argument (whatever is its type) and generate an std::string, like this:
template<typename T>
std::string
my_to_string(T) { return ("what is this type?"); }
template<>
std::string
my_to_string(std::string s) { return (s); }
template<>
std::string
my_to_string(int integer) { return (std::to_string(integer)); }
and then call it in your log function like this: std::cout << my_to_string(value1);