Here's a rundown on static_cast<>
and dynamic_cast<>
specifically as they pertain to pointers. This is just a 101-level rundown, it does not cover all the intricacies.
static_cast< Type* >(ptr)
This takes the pointer in ptr
and tries to safely cast it to a pointer of type Type*
. This cast is done at compile time. It will only perform the cast if the types are related. If the types are not related, you will get a compiler error. For example:
class B {};
class D : public B {};
class X {};
int main()
{
D* d = new D;
B* b = static_cast<B*>(d); // this works
X* x = static_cast<X*>(d); // ERROR - Won't compile
return 0;
}
dynamic_cast< Type* >(ptr)
This again tries to take the pointer in ptr
and safely cast it to a pointer of type Type*
. But this cast is executed at runtime, not compile time. Because this is a run-time cast, it is useful especially when combined with polymorphic classes. In fact, in certain cases the classes must be polymorphic in order for the cast to be legal.
Casts can go in one of two directions: from base to derived (B2D) or from derived to base (D2B). It's simple enough to see how D2B casts would work at runtime. Either ptr
was derived from Type
or it wasn't. In the case of D2B dynamic_cast<>s, the rules are simple. You can try to cast anything to anything else, and if ptr
was in fact derived from Type
, you'll get a Type*
pointer back from dynamic_cast
. Otherwise, you'll get a NULL pointer.
But B2D casts are a little more complicated. Consider the following code:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void DoIt() = 0; // pure virtual
virtual ~Base() {};
};
class Foo : public Base
{
public:
virtual void DoIt() { cout << "Foo"; };
void FooIt() { cout << "Fooing It..."; }
};
class Bar : public Base
{
public :
virtual void DoIt() { cout << "Bar"; }
void BarIt() { cout << "baring It..."; }
};
Base* CreateRandom()
{
if( (rand()%2) == 0 )
return new Foo;
else
return new Bar;
}
int main()
{
for( int n = 0; n < 10; ++n )
{
Base* base = CreateRandom();
base->DoIt();
Bar* bar = (Bar*)base;
bar->BarIt();
}
return 0;
}
main()
can't tell what kind of object CreateRandom()
will return, so the C-style cast Bar* bar = (Bar*)base;
is decidedly not type-safe. How could you fix this? One way would be to add a function like bool AreYouABar() const = 0;
to the base class and return true
from Bar
and false
from Foo
. But there is another way: use dynamic_cast<>
:
int main()
{
for( int n = 0; n < 10; ++n )
{
Base* base = CreateRandom();
base->DoIt();
Bar* bar = dynamic_cast<Bar*>(base);
Foo* foo = dynamic_cast<Foo*>(base);
if( bar )
bar->BarIt();
if( foo )
foo->FooIt();
}
return 0;
}
The casts execute at runtime, and work by querying the object (no need to worry about how for now), asking it if it the type we're looking for. If it is, dynamic_cast<Type*>
returns a pointer; otherwise it returns NULL.
In order for this base-to-derived casting to work using dynamic_cast<>
, Base, Foo and Bar must be what the Standard calls polymorphic types. In order to be a polymorphic type, your class must have at least one virtual
function. If your classes are not polymorphic types, the base-to-derived use of dynamic_cast
will not compile. Example:
class Base {};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile
return 0;
}
Adding a virtual function to base, such as a virtual dtor, will make both Base and Der polymorphic types:
class Base
{
public:
virtual ~Base(){};
};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // OK
return 0;
}
static_cast
provided that you know (by design of your program) that the thing pointed to really is an int
.
static_cast is designed to reverse any implicit conversion. You converted to void*
implicitly, therefore you can (and should) convert back with static_cast
if you know that you really are just reversing an earlier conversion.
With that assumption, nothing is being reinterpreted - void
is an incomplete type, meaning that it has no values, so at no point are you interpreting either a stored int value "as void" or a stored "void value" as int. void*
is just an ugly way of saying, "I don't know the type, but I'm going to pass the pointer on to someone else who does".
reinterpret_cast
if you've omitted details that mean you might actually be reading memory using a type other than the type is was written with, and be aware that your code will have limited portability.
By the way, there are not very many good reasons for using a void*
pointer in this way in C++. C-style callback interfaces can often be replaced with either a template function (for anything that resembles the standard function qsort
) or a virtual interface (for anything that resembles a registered listener). If your C++ code is using some C API then of course you don't have much choice.
Best Answer
static_cast
static_cast
is the first cast you should attempt to use. It does things like implicit conversions between types (such asint
tofloat
, or pointer tovoid*
), and it can also call explicit conversion functions (or implicit ones). In many cases, explicitly statingstatic_cast
isn't necessary, but it's important to note that theT(something)
syntax is equivalent to(T)something
and should be avoided (more on that later). AT(something, something_else)
is safe, however, and guaranteed to call the constructor.static_cast
can also cast through inheritance hierarchies. It is unnecessary when casting upwards (towards a base class), but when casting downwards it can be used as long as it doesn't cast throughvirtual
inheritance. It does not do checking, however, and it is undefined behavior tostatic_cast
down a hierarchy to a type that isn't actually the type of the object.const_cast
const_cast
can be used to remove or addconst
to a variable; no other C++ cast is capable of removing it (not evenreinterpret_cast
). It is important to note that modifying a formerlyconst
value is only undefined if the original variable isconst
; if you use it to take theconst
off a reference to something that wasn't declared withconst
, it is safe. This can be useful when overloading member functions based onconst
, for instance. It can also be used to addconst
to an object, such as to call a member function overload.const_cast
also works similarly onvolatile
, though that's less common.dynamic_cast
dynamic_cast
is exclusively used for handling polymorphism. You can cast a pointer or reference to any polymorphic type to any other class type (a polymorphic type has at least one virtual function, declared or inherited). You can use it for more than just casting downwards – you can cast sideways or even up another chain. Thedynamic_cast
will seek out the desired object and return it if possible. If it can't, it will returnnullptr
in the case of a pointer, or throwstd::bad_cast
in the case of a reference.dynamic_cast
has some limitations, though. It doesn't work if there are multiple objects of the same type in the inheritance hierarchy (the so-called 'dreaded diamond') and you aren't usingvirtual
inheritance. It also can only go through public inheritance - it will always fail to travel throughprotected
orprivate
inheritance. This is rarely an issue, however, as such forms of inheritance are rare.reinterpret_cast
reinterpret_cast
is the most dangerous cast, and should be used very sparingly. It turns one type directly into another — such as casting the value from one pointer to another, or storing a pointer in anint
, or all sorts of other nasty things. Largely, the only guarantee you get withreinterpret_cast
is that normally if you cast the result back to the original type, you will get the exact same value (but not if the intermediate type is smaller than the original type). There are a number of conversions thatreinterpret_cast
cannot do, too. It's often abused for particularly weird conversions and bit manipulations, like turning a raw data stream into actual data, or storing data in the low bits of a pointer to aligned data. For those cases, seestd::bit_cast
.C-Style Cast and Function-Style Cast
C-style cast and function-style cast are casts using
(type)object
ortype(object)
, respectively, and are functionally equivalent. They are defined as the first of the following which succeeds:const_cast
static_cast
(though ignoring access restrictions)static_cast
(see above), thenconst_cast
reinterpret_cast
reinterpret_cast
, thenconst_cast
It can therefore be used as a replacement for other casts in some instances, but can be extremely dangerous because of the ability to devolve into a
reinterpret_cast
, and the latter should be preferred when explicit casting is needed, unless you are surestatic_cast
will succeed orreinterpret_cast
will fail. Even then, consider the longer, more explicit option.C-style casts also ignore access control when performing a
static_cast
, which means that they have the ability to perform an operation that no other cast can. This is mostly a kludge, though, and in my mind is just another reason to avoid C-style casts.std::bit_cast
[C++20]std::bit_cast
copies the bits and bytes of the source object (its representation) directly into a new object of the target type. It's a standards-compliant way to do type punning. If you find yourself writing*reinterpret_cast<SomeType*>(&x)
, you probably should usestd::bit_cast<SomeType>(x)
instead.std::bit_cast
is declared in<bit>
. The objects must be the same size and be trivially copyable. If you can't yet use C++20, usememcpy
to copy the source value into a variable of the desired type.