First of all, this is not a duplicate of Why do we have reinterpret_cast in C++ when two chained static_cast can do it's job?.
I know situations where we cannot use even two chained static_cast
to achieve that, what reinterpret_cast
does. But is there any situation where I should prefer a two chained static_cast
over a simple and more readable reinterpret_cast
?
reinterpret_cast
should be a huge flashing symbol that says THIS LOOKS CRAZY BUT I KNOW WHAT I'M DOING. Don't use it just out of laziness.
reinterpret_cast
means "treat these bits as ..." Chained static casts are not the same because they may modify their targets according to the inheritence lattice.
struct A {
int x;
};
struct B {
int y;
};
struct C : A, B {
int z;
};
C c;
A * a = &c;
int main () {
assert (reinterpret_cast <B *> (a) != static_cast <B *> (static_cast <C *> (a)));
}
If you are not 100% sure that a
points to a b
, use dynamic_cast
which will search for the above solution (albeit with a runtime cost). Bear in mind that this may return NULL or throw on failure.
I'm trying to think of times when I've actually used reinterpret_cast
, there are really only two:
- when a function is zipping/encrypting an arbitrary buffer and I want to use a
const char *
to traverse it
if(*reinterpret_cast<uint32_t*>(array_of_4_bytes_A) < *reinterpret_cast<uint32_t*>(array_of_4_bytes_B)
or somesuch. Lines like this invite scrutiny and demand comments.
Otherwise if you have a A*
which is really a B*
then you probably want a union.
I for one would rather see reinterpret_cast <TargetType> (pointer_of_some_other_type)
than static_cast <TargetType> (static_cast <void*> (pointer_of_some_other_type))
or static_cast <TargetType> ((void*) (pointer_of_some_other_type))
any time. That chain of casts going through void* is just a sneaky, underhanded way to avoid using the dreaded reinterpret_cast.
Many projects ban the use of reinterpret_cast unless a waiver is granted; the person who wrote the code needs to justify the use of the cast. IMO, a chain of static casts is worse (much worse!) than is reinterpret_cast. The chain has the same effects, the same problems as does a reinterpret_cast, but the chain does not have the benefit of being easy to find with a grep.
Addendum
Look at it this way. Case 1, you use reinterpret_cast, you go through all the project hoops to justify its use, the project manager grants a waiver. Months later, an error is traced to your use of dynamic_cast. You have a get out of jail free card. It is the project manager's ass that is on the line for giving you that card.
Case 2, you use the sneaky, underhanded chain of static casts and the code sneaks through peer review unscathed. Months later, an error is traced to your use of despicable technique. Your project manager might be in a bit of trouble for not catching this nastiness, but it is your ass that is on the line. You do not have that get out of jail free card. You do not pass Go. You go directly to the unemployment line.
Always use reinterpret cast as a last resort - it doesn't do any checking! - so if you can chain two, three or ten statements together that do some kind of validation on the operation then you've gained something worthwhile.
Since, the listing of the scenarios can go very long, I am putting in simple words:
If chained static_cast<>
s don't give compile-error, you should avoid reinterpret_cast<>
.
You should not use reinterpret_cast
in cases where cross-cast pointers - instead use implicit conversion to void*
, then static_cast
.
Because in theory, they could do something different (although it's hard to imagine such a case). More importantly, they send different signals to the reader, and tell a different story to the compiler (which could affect optimization). Logically, I'd say use chained static_cast
through void*
for cases to/from a character type (e.g. dumping an image of the raw memory), and other cases which are well defined and portable, and reinterpret_cast
when you're doing real low level, hardware dependent work: extracting the exponent field of a float
by casting its address to an unsigned int*
, and bit masking, for example.