C++ When should we prefer to use a two chained sta

2019-01-12 09:14发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

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



回答5:

You should not use reinterpret_cast in cases where cross-cast pointers - instead use implicit conversion to void*, then static_cast.



回答6:

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.