Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I static_cast between char * and unsigned char *?

Apparently the compiler considers them to be unrelated types and hence reinterpret_cast is required. Why is this the rule?

like image 773
Nick Avatar asked Apr 14 '12 07:04

Nick


People also ask

Can static_cast fail?

static_cast can't throw exception since static_cast is not runtime cast, if some cannot be casted, code will not compiles. But if it compiles and cast is bad - result is undefined.

Can you static_cast int to char?

There is a well-defined conversion from int to char ; that's what static_cast does. In fact, you don't need the cast; you can just use an assignment here.

Can you cast unsigned char to char?

Semantically, passing between unsigned char * and char * are safe, and even though casting between them, so as in c++. All the code inside process_unsigned and process are just IDENTICAL. The only difference is unsigned and signed.

What does static_cast void do?

If, in your code, you know you do not need a result somewhere, you can use the static_cast<void> method to mark the result as discarded – but the compiler will consider the variable used then and no longer create a warning or error.


2 Answers

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 = new 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.

like image 78
EdChum Avatar answered Sep 24 '22 00:09

EdChum


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.

like image 32
Tobias Langner Avatar answered Sep 24 '22 00:09

Tobias Langner