C++ Casts

C++ provides five specific casts:

Note that the old C-style casts such as (int) myFloat still work in C++ and are still used extensively in existing code bases. C-style casts cover all C++ casts except bit_cast() and thus are more error-prone because it's not always obvious what you are trying to achieve, and you might end up with unexpected results. I strongly recommend you only use the C++ style casts in new code because they are safer and stand out better syntactically and visually in your code.

const_cast()

static_cast()

You can use static_cast() to perform explicit conversions that are supported directly by the language. For example, if you write an arithmetic expression in which you need to convert an int to a double to avoid integer division, use a static_cast(). In this example, it's enough to only use static_cast() with i, because that makes one of the two operands a double, making sure C++ performs floating-point division.

int i { 3 };
int j { 4 };
double result { static_cast<double>(i) / j };

You can also use static_cast() to perform explicit conversions that are allowed because of user-defined constructors or conversion routines. For example, if class A has a constructor that takes an object of class B, you can convert a B object to an A object using a static_cast(). In most situations where you want this behavior, however, the compiler performs the conversion automatically.

Another use for static_cast() is to perform downcasts in an inheritance hierarchy, as in this example:

class Base
{
public:
  virtual ˜Base() = default;
};

class Derived : public Base
{
public:
  virtual ˜Derived() = default;
};

int main()
{
  Base* b { nullptr };
  Derived* d { new Derived {} };
  b = d; // Don't need a cast to go up the inheritance hierarchy.
  d = static_cast<Derived*>(b); // Need a cast to go down the hierarchy.

  Base base;
  Derived derived;
  Base& br { derived };
  Derived& dr { static_cast<Derived&>(br) };
}

These casts work with both pointers and references. They do not work with objects themselves.

Note that casts using static_cast() do not perform run-time type checking. They allow you to convert any Base pointer to a Derived pointer, or Base reference to a Derived reference, even if the Base really isn't a Derived at run time. For example, the following code compiles and executes, but using the pointer d can result in potentially catastrophic failure, including memory overwrites outside the bounds of the object.

Base* b { new Base {} };
Derived* d { static_cast<Derived*>(b) };

To perform such casts safely with run-time type checking, use dynamic_cast().

static_cast() is not all-powerful. You can't static_cast() pointers of one type to pointers of another unrelated type. You can't directly static_cast() objects of one type to objects of another type if there is no converting constructor available. You can't static_cast() a const type to a non-const type. You can't static_cast() pointers to ints. Basically, you can only do things that make sense according to the type rules of C++.

reinterpret_cast()

reinterpret_cast() is a bit more powerful, and concomitantly less safe, than static_cast(). You can use it to perform some casts that are not technically allowed by the C++ type rules but that might make sense to the programmer in some circumstances.

For example, you can use reinterpret_cast() to cast a reference to one type to a reference to another type, even if the types are unrelated. Similarly, you can use it to cast a pointer type to any other pointer type, even if they are unrelated by an inheritance hierarchy. However, casting a pointer to a void* can be done implicitly, without an explicit cast. To cast a void* back to a correctly typed pointer, a static_cast() is enough. A void* pointer is just a pointer to some location in memory. No type information is associated with a void* pointer. Here are some examples:

class X {};
class Y {};

int main() {
  X x;
  Y y;
  X* xp { &x };
  Y* yp { &y };
  // Need reinterpret_cast for pointer conversion from unrelated classes
  // static_cast doesn't work.
  xp = reinterpret_cast<X*>(yp);
  // No cast required for conversion from pointer to void*
  void* p { xp };
  // static_cast is enough for pointer conversion from void*
  xp = static_cast<X*>(p);
  // Need reinterpret_cast for reference conversion from unrelated classes
  // static_cast doesn't work.
  X&xr { x };
  Y&yr { reinterpret_cast<Y&>(x) };
}

reinterpret_cast() is not all-powerful; it comes with quite a few restrictions on what can be cast to what. These restrictions are not further discussed here, as I recommend that you use these kinds of casts judiciously.

In general, you should be careful with reinterpret_cast() because it allows you to do conversions without performing any type checking.

WARNING: You can also use reinterpret_cast() to cast pointers to integral types and back. However, you can only cast a pointer to an integral type that is large enough to hold it. For example, trying to use reinterpret_cast() to cast a 64-bit pointer to a 32-bit integer results in a compilation error.

dynamic_cast()

dynamic_cast() provides a run-time check on casts within an inheritance hierarchy. You can use it to cast pointers or references. dynamic_cast() checks the run-time type information of the underlying object at run time. If the cast doesn't make sense, dynamic_cast() returns a null pointer (for the pointer version) or throws an std::bad_cast exception (for the reference version).

For example, suppose you have the following class hierarchy:

class Base {
public:
  virtual ˜Base() = default;
};

class Derived : public Base {
public:
  virtual ˜Derived() = default;
};

The following example shows a correct use of dynamic_cast():

Base* b;
Derived* d { new Derived {} };
b = d;
d = dynamic_cast<Derived*>(b);

The following dynamic_cast() on a reference will cause an exception to be thrown:

Base base;
Derived derived;
Base& br { base };
try {
  Derived& dr { dynamic_cast<Derived&>(br) };
} catch (const bad_cast&) {
  println("Bad cast!");
}

Note that you can perform the same casts down the inheritance hierarchy with a static_cast() or reinterpret_cast(). The difference with dynamic_cast() is that it performs run-time (dynamic) type checking, while static_cast() and reinterpret_cast() perform the cast even if they are erroneous.

Remember, the run-time type information is stored in the vtable of an object. Therefore, to use dynamic_cast(), your classes must have at least one virtual member function. If your classes don't have a vtable, trying to use dynamic_cast() will result in a compilation error.

std::bit_cast()

std::bit_cast() is defined in <bit>. It's the only cast that's part of the Standard Library; the other casts are part of the C++ language itself. bit_cast() resembles reinterpret_cast(), but it creates a new object of a given target type and copies the bits from a source object to this new object. It effectively interprets the bits of the source object as if they are the bits of the target object. bit_cast() requires that the size of the source and target objects are the same and that both are trivially copyable.

NOTE: A trivially copyable type is a type of which the underlying bytes making up the object can be copied into an array of, for example, char. If the data of that array is then copied back into the object, the object keeps its original value.

Here is an example:

float asFloat { 1.23f };
auto asUint { bit_cast<unsigned int>(asFloat) };
if (bit_cast<float>(asUint) == asFloat) { println("Roundtrip success."); }

A use case for bit_cast() is with binary I/O of trivially copyable types. For example, you can write the individual bytes of such types to a file. When you read the file back into memory, you can use bit_cast() to correctly interpret the bytes read from the file.