I l@ve RuBoard Previous Section Next Section

Solution

graphics/bulb_icon.gif

Let's answer the questions one by one.

  1. Which of the following new-style casts are not equivalent to a C-style cast?

    Only dynamic_cast is not equivalent to a C-style cast. All other new-style casts have old-style equivalents.

    Guideline

    graphics/guideline_icon.gif

    Prefer new-style casts.


  2. For each of the following C-style casts, write the equivalent new-style cast. Which are incorrect if not written as a new-style cast?

    
    
    void f() 
    
    
    {
    
    
      A* pa; B* pb; C* pc;
    
    
      pa = (A*)&ra1;
    
    
    

    Use const_cast instead:

    
    
    pa = const_cast<A*>(&ra1); 
    
    
    
    
    
    pa = (A*)&a2;
    
    
    

    This cannot be expressed as a new-style cast. The closest candidate is const_cast, but because a2 is a const object, the results of using the pointer are undefined.

    
    
    pb = (B*)&c1; 
    
    
    

    Use reinterpret_cast instead:

    
    
      pb = reinterpret_cast<B*>(&c1); 
    
    
    
    
    
      pc = (C*)&d1;
    
    
    }
    
    
    

    The above cast is wrong in C. In C++, no cast is required:

    
    
    pc = &d1; 
    
    
    
  3. 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

    graphics/guideline_icon.gif

    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

    graphics/guideline_icon.gif

    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

    graphics/guideline_icon.gif

    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.

  4. Why is it normally unuseful to const_cast from non-const to const?

    The first three questions included no examples of using const_cast to add const, for example, to convert a pointer to non-const to a pointer to const. After all, explicitly adding const is usually redundant梖or example, it's already legal to assign a pointer to non-const to a pointer to const. Normally, we only need const_cast to do the reverse.

And the last part of the question: Demonstrate a valid example where it can be useful to const_cast from non-const to const.

There is at least one case in which you could usefully const_cast from non-const to const梩o call a specific overloaded function or a specific version of a template. For example:



void f( T& ); 


void f( const T& );


template<class T> void g( T& t )


{


  f( t );                       // calls f(T&)


  f( const_cast<const T&>(t) ); // calls f(const T&)


}


Of course, in the case of choosing a specific version of a template, it's usually just easier to name it explicitly instead of forcing the right deduction. For example, to call the right version of a templated function h(), writing "h<const T&>( t )" is preferable to writing "h( const_cast<const T&>(t) )".

    I l@ve RuBoard Previous Section Next Section