Effective C++ item 27: Minimize Casting

There are usually three different ways to write the same cast.

1
2
3
(T)expression
// or
T(expression)

There is no difference in meaning between these forms; it’s purely a matter of where you put the parentheses. I call these two forms old-style casts.
C++ also offers four new cast forms (often called new-style or C++-style casts):

1
2
3
4
const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)

Each serves a distinct purpose:

  • const_cast is typically used to cast away the constness of objects. It is the only C++-style cast that can do this.
  • dynamic_cast is primarily used to perform “safe downcasting,” i.e., to determine whether an object is of a particular type in an inheritance hierarchy. It is the only cast that cannot be performed using the old-style syntax. It is also the only cast that may have a significant runtime cost.
  • reinterpret_cast is intended for low-level casts that yield implementation-dependent (i.e., unportable) results, e.g., casting a pointer to an int. Such casts should be rare outside low-level code.
  • static_cast can be used to force implicit conversions (e.g., non-const object to const object, int to double, etc.). It can also be used to perform the reverse of many such conversions (e.g., void* pointers to typed pointers, pointer-to-base to pointer-to-derived), though it cannot cast from const to non-const objects. (Only const_cast can do that.)

The general rule is to avoid using cast as much as possible. const_cast is almost aways a bad idea because it breaks const semantics. dynamic_cast is a indication of bad inheritance design or mis-use of inheritance. For example, following code should never happen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Window { ... };
... // derived classes are defined here
typedef std::vector<std::tr1::shared_ptr<Window> > VPW;
VPW winPtrs;
for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)
{
if (SpecialWindow1 *psw1 =
dynamic_cast<SpecialWindow1*>(iter->get())) { ... }
else if (SpecialWindow2 *psw2 =
dynamic_cast<SpecialWindow2*>(iter->get())) { ... }
else if (SpecialWindow3 *psw3 =
dynamic_cast<SpecialWindow3*>(iter->get())) { ... }
...
}

static_cast is sometimes ok to use, but it also has caveat since it creates a new temporary object. Considering following code

1
2
3
4
5
6
7
8
9
10
11
12
class Window {
public:
virtual void onResize() { ... }
...
};
class SpecialWindow: public Window {
public:
virtual void onResize() {
static_cast<Window>(*this).onResize();
...
}
};

You would think static_cast<Window>(*this).onResize(); is equivalent to Window::onResize();, but it’s not. static_cast<Window>(*this) will create a new temporary object and onResize() will be invoked on that temporary object instead of this object.

Things to remember:

  • Avoid casts whenever practical, especially dynamic_casts in performance-sensitive code. If a design requires casting, try to develop a cast-free alternative.
  • When casting is necessary, try to hide it inside a function. Clients can then call the function instead of putting casts in their own code.
  • Prefer C++-style casts to old-style casts. They are easier to see, and they are more specific about what they do.

Reference:
“Effective C++” Third Edition by Scott Meyers.