Previous Next Contents Index Doc Set Home


Cast Operations

6


This chapter explains the new cast operations: const and volatile casts, reinterpret cast, static and dynamic casts.

The emerging C++ standard defines new cast operations that provide finer control over casting than previous cast operations. The dynamic_cast<> operator provides a way to check the actual type of a pointer to a polymorphic class. Otherwise, the new casts all perform a subset of the casts allowed by the classic cast notation. For example, const_cast<int*>v could be written (int*)v. The new casts simply categorize the variety of operations available to express the programmer's intent more clearly and allow the compiler to better check the code.


Cast Operations Options

To enable recognition of the cast operators, use the option
-features=castop
, which is the default. To disable recognition of the cast operators, use the option -features=no%castop.


Const and Volatile Cast

The expression const_cast<T>(v) can be used to change the "const" or "volatile" qualifiers of pointers or references. T must be a pointer, reference, or pointer to member type. If cv1 and cv2 are some combination of const and volatile qualifiers (that is, cv1 is volatile and cv2 is const volatile), const_cast can convert a value of type "pointer to cv1 T" to "pointer to cv2 T", or "pointer to member of type cv1 T" to "pointer to member of type cv2 T". If we have an lvalue of type cv1 T, then const_cast can convert it to "reference to type cv2 T". (An lvalue names an object in such a way that its address can be taken.)

class A { public: virtual void f();
                  int i; };
extern const int A::* cimp;
extern const volatile int* cvip;
extern int* ip;
void use_of_const_cast( )
    { const A a1;
      const_cast<A&>(a1).f( );   // remove const
      a1.*(const_cast<int A::*> cimp) = 1;    // remove const
      ip = const_cast<int*> cvip; }   // remove const and volatile


Reinterpret Cast

The expression reinterpret_cast<T>(v)changes the interpretation of the value of the expression v. It will convert from pointer types to integers and back again, between two unrelated pointers, pointers to members, or pointers to functions. The only guarantee on such casts is that a cast to a new type, followed by a cast back to the original type, will have the original value. It is legal to cast an lvalue of type T1 to type T2& if a pointer of type T1* can be converted to a pointer of type T2* by a reinterpret_cast. reinterpret_cast cannot be used to convert between pointers to two different classes that are related by inheritance (use static_cast or dynamic_cast), nor can it be used to cast away const (use const_cast).

class A { public: virtual void f( ); };
void use_of_reinterpret_cast( )
    { A a1;
      const A a2;
      int i = reinterpret_cast<int>(&a1);   // grab address
      const int j = reinterpret_cast<int>(&a2); }  // grab address


Static Cast

The expression static_cast<T>(v) converts the value of the expression v to that of type T. It can be used for any cast that is performed implicitly on assignment. In addition, any value may be cast to void, and any implicit cast can be reversed if that cast would be legal as an old-style cast. It cannot be used to cast away const.

class B            { public: virtual void g( ); };
class C : public B { public: virtual void g( ); };

void use_of_static_cast( )
    { C c;
      // an explicit temporary lvalue to the base of c, a B
      B& br = c;
      br.g( );   // call B::g instead of C::g
      // a static_cast of an lvalue to the base of c, a B
      static_cast<B&>(c).g( ); }   // call B::g instead of C::g


Dynamic Cast

A pointer or reference to a class can actually point to any class publicly derived from that class. Occasionally, it may be desirable to obtain a pointer to the fully-derived class, or to some other base class for the object. The dynamic cast provides this facility.

The dynamic type cast will convert a pointer or reference to one class into a pointer or reference to another class. That second class must be the fully-derived class of the object, or a base class of the fully-derived class.

In the expression dynamic_cast<T>(v), v is the expression to be cast, and T is the type to which it should be cast. T must be a pointer or reference to a complete class type, or "pointer to cv void", where cv is [const][volatile]. In the case of pointer types, if the specified class is not a base of the fully derived class, the cast returns a null pointer. In the case of reference types, if the specified class is not a base of the fully derived class, the cast throws a bad_cast exception. For example, given the class definitions:

class A          { public: virtual void f( ); };
class B          { public: virtual void g( ); };
class AB :       public virtual A, private B { };

The following function will succeed.

void simple_dynamic_casts( )
    { AB  ab;
      B*  bp  = (B*)&ab;  // cast needed to break protection
      A*  ap  = &ab;      // public derivation, no cast needed
      AB& abr = dynamic_cast<AB&>(*bp);  // succeeds
      ap = dynamic_cast<A*>(bp);         assert( ap != NULL );
      bp = dynamic_cast<B*>(ap);         assert( bp == NULL );
      ap = dynamic_cast<A*>(&abr);       assert( ap != NULL );
      bp = dynamic_cast<B*>(&abr);       assert( bp == NULL ); }

In the presence of virtual inheritance and multiple inheritance of a single base class, the actual dynamic cast must be able to identify a unique match. If the match is not unique, the cast fails. For example, given the additional class definitions:

class AB_B :     public AB,        public B  { };
class AB_B__AB : public AB_B,      public AB { };

The following function will succeed:

void complex_dynamic_casts( )
    { 
      AB_B__AB ab_b__ab;
      A*ap = &ab_b__ab; 
                    // okay: finds unique A statically
      AB*abp = dynamic_cast<AB*>(ap);   
                    // fails: ambiguous 
      assert( abp == NULL );
             // STATIC ERROR: AB_B* ab_bp = (AB_B*)ap; 
                    // not a dynamic cast
      AB_B*ab_bp = dynamic_cast<AB_B*>(ap); 
                    // dynamic one is okay
      assert( ab_bp != NULL ); 
     }

The null-pointer error return of dynamic_cast is useful as a condition between two bodies of code, one to handle the cast if the type guess is correct, and one if it is not.

void using_dynamic_cast( A* ap )
    { 
      if ( AB *abp = dynamic_cast<AB*>(ap) )
          { // abp is non-null,
            // so ap was a pointer to an AB object
            // go ahead and use abp
            process_AB( abp ); }
      else
          { // abp is null,
            // so ap was NOT a pointer to an AB object
            // do not use abp
            process_not_AB( ap ); 
     }

If run-time type information has been disabled, i.e. -features=no%rtti, (See Chapter 5, "RTTI"), the compiler converts dynamic_cast to static_cast and issues a warning.

If exceptions have been disabled (See Chapter 4, "Exception Handling"), the compiler converts dynamic_cast<T&> to static_cast<T&> and issues a warning. The dynamic cast to a reference may require an exception in normal circumstances.

Dynamic cast is necessarily slower than an appropriate design pattern, such as conversion by virtual functions. See Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma et al.




Previous Next Contents Index Doc Set Home