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.