Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is static_cast of void* to another type allowed?

I was just reading this thread: Simple c++ pointer casting

And that got me to thinking why a static_cast between different pointer types is not allowed (except in the cases in which it is) unless you static_cast to a void* as an intermediary step. It seems to me that either both or neither should be allowed. Here is an example:

char*          cs;
unsigned char* ucs;

cs = reinterpret_cast<char*>(ucs);                  // 1) allowed, of course
cs = static_cast<char*>(ucs);                       // 2) not allowed: incompatible pointer types
cs = static_cast<char*>( static_cast<void*>(ucs) ); // 3) now it's allowed!

It seems to me that if #3 is possible then #2 should be allowed as well. Or conversely, if #2 is not allowed on the grounds that the pointers are not compatible (necessitating a reinterpret_cast) then perhaps static_casting from a void* to anything should not be allowed on the grounds of pointer incompatibility. (Casting to a void* from any other pointer is always okay, of course.)

So why isn't one of those possilities true - that #2 and #3 are either both allowed or neither allowed? Why does it instead work as shown in my example?

like image 381
Dave Lillethun Avatar asked Jul 09 '13 21:07

Dave Lillethun


3 Answers

cs = static_cast<char*>( static_cast<void*>(ucs) ); // 3) now it's allowed!

It will compile. Does it mean that it is allowed? Not. The standard allows conversions from void* to T* only in the case where the void* was obtained by a conversion from T*, which is not your case.

like image 137
David Rodríguez - dribeas Avatar answered Dec 21 '22 12:12

David Rodríguez - dribeas


The problem is this:

my_type *p = new my_type;
void *vp = p; // okay

Now if you need to get the original pointer back you do this:

my_type *new_p = static_cast<my_type*>(vp); // okay

That's fine, and the result is well defined. You get undefined behavior if you use static_cast to convert the pointer to a different type than the original pointer, and that's not something the compiler can, in general, detect. So the cast is allowed, and if you misuse it, it's on your head.

like image 37
Pete Becker Avatar answered Dec 21 '22 11:12

Pete Becker


Okay, I'm going to take a stab at answering my own question here because I think I'm getting an idea, although I'm not sure it's right. You can let me know if you think I'm getting it by up/down voting my answer, I guess. :)

So I think static_cast is telling the compiler, "Trust me, this is safe." Whereas reinterpret_cast is telling the compiler, "I know this is unsafe; do it anyway."

So going back to my examples:

cs = reinterpret_cast<char*>(ucs);

is unsafe, but the compiler will do it anyway because you told it to.

cs = static_cast<char*>(ucs);

will result in a compiler error because you said, "Trust me, this is safe," but the compiler knows that it really is not. So it doesn't trust you because it actually knows in this case, and therefore errors.

Now the last example has two parts:

void* temp = static_cast<void*>(ucs);

is allowed of course because casting to void* is safe.

cs = static_case<char*>(temp); // remember temp is a void*

is allowed because casting from a void* is sometimes safe and sometimes not - the compiler cannot tell which it is in any given instance... so when you say, "Trust me, this is safe," the compiler trusts you - and if you're wrong, then that's on your head. O.O

So in other words, you cannot static_cast something that is never a safe case - that's when you need reinterpret_cast ... but you can static_cast something that is sometime safe and sometimes not (and the compiler can't tell which is the case in any given instance).

All that said... I refered to "safe" and "unsafe" casts a lot here, but what do those words actually mean? (And so, to some extent, I still don't even understand my own answer here...! Please comment if you can help me figure that out...)

like image 43
Dave Lillethun Avatar answered Dec 21 '22 12:12

Dave Lillethun