Critique each of the following C++ casts for style and correctness.
First, a general note: All of the following dynamic_casts would be errors if the classes involved did not have virtual functions. Fortunately, A does provide a virtual function, making all the dynamic_casts legal.
void g()
{
unsigned char* puc = static_cast<unsigned char*>(&c);
signed char* psc = static_cast<signed char*>(&c);
Error: We must use reinterpret_cast for both cases. This might surprise you at first, but the reason is that char, signed char, and unsigned char are three distinct types. Even though there are implicit conversions between them, they are unrelated, so pointers to them are unrelated.
void* pv = static_cast<void*> (&b1);
B* pb1 = static_cast<B*>(pv);
These are both fine, but the first is unnecessary, because there is already an implicit conversion from a data pointer to a void*.
B* pb2 = static_cast<B*> (&b1);
This is fine, but unnecessary, since the argument is already a B*.
A* pa1 = const_cast<A*>(&ra1);
This is legal, but casting away const (or volatile) is usually indicative of poor style. Most of the cases in which you legitimately would want to remove the const-ness of a pointer or reference are related to class members and covered by the mutable keyword. See Item 43 for further discussion of const-correctness.
Guideline
 |
Avoid casting away const. Use mutable instead.
|
A* pa2 = const_cast<A*>(&ra2);
Error: This will produce undefined behavior if the pointer is used to write on the object, because a2 really is a const object. To see why this is a legitimate problem, consider that a compiler is allowed to see that a2 is created as a const object and use that information to store it in read-only memory as an optimization. Casting away const on such an object is obviously dangerous.
Guideline
 |
Avoid casting away const.
|
B* pb3 = dynamic_cast<B*>(&c1);
Potential error (if you try to use pb3): Because c1 IS-NOT-A B (because C is not publicly derived from B梚n fact, it is not derived from B at all), this will set pb3 to null. The only legal cast would be a reinterpret_cast, and using that is almost always evil.
A* pa3 = dynamic_cast<A*>(&b1);
Probable error: Because b1 IS-NOT-AN A (because B is not publicly derived from A; its derivation is private), this is illegal unless g() is a friend of B.
B* pb4 = static_cast<B*>(&d1);
This is fine, but unnecessary because derived-to-public-base pointer conversions can be done implicitly.
D* pd = static_cast<D*>(pb4);
This is fine, which may surprise you if you expected this to require a dynamic_cast. The reason is that downcasts can be static when the target is known, but beware: You are telling the compiler that you know for a fact that what is being pointed to really is of that type. If you are wrong, then the cast cannot inform you of the problem (as could dynamic_cast, which would return a null pointer if the cast failed) and, at best, you will get spurious run-time errors and/or program crashes.
Guideline
 |
Avoid downcasts.
|
pa1 = dynamic_cast<A*>(pb2);
pa1 = dynamic_cast<A*>(pb4);
These two look very similar. Both attempt to use dynamic_cast to convert a B* into an A*. However, the first is an error, while the second is not.
Here's the reason: As noted above, you cannot use dynamic_cast to cast a pointer to what really is a B object (and here pb2 points to the object b1) into an A object, because B inherits privately, not publicly, from A. However, the second cast succeeds because pb4 points to the object d1, and D does have A as an indirect public base class (through C), and dynamic_cast is able to cast across the inheritance hierarchy using the path B*
D*
C*
A*.
C* pc1 = dynamic_cast<C*>(pb4);
This, too, is fine for the same reason as the last: dynamic_cast can navigate the inheritance hierarchy and perform cross-casts, so this is legal and will succeed.
C& rc1 = dynamic_cast<C&>(*pb2);
}
Finally, an "exceptional" error: Because *pb2 isn't really a C, dynamic_cast will throw a bad_cast exception to signal failure. Why? Well, dynamic_cast can and does return null if a pointer cast fails, but since there's no such thing as a null reference, it can't return a null reference if a reference cast fails. There's no way to signal such a failure to the client code besides throwing an exception, so that's what the standard bad_cast exception class is for.