Type casting in C++
C++ has four named casts, each with a precise job. Knowing which to reach for, and why the old C-style cast hides danger, is core C++ literacy.
Why four casts instead of one
A C-style cast, (Type)value: will attempt almost any conversion and tells you nothing about
which kind it performed. C++ replaced it with four named casts so your intent is explicit and the
compiler can reject conversions you didn’t actually mean. Each one is searchable, each one means a
specific thing, and each one fails loudly when misused.
static_cast, the everyday conversion
static_cast<T>(v) performs well-defined conversions the compiler can check at compile time:
numeric conversions (double → int), void* back to a typed pointer, and
casts up or down a class hierarchy when you already know the runtime type. It does no
runtime check on downcasts, so it’s fast, but a wrong downcast is undefined behavior.
dynamic_cast, the checked downcast
dynamic_cast<T>(v) safely converts within a polymorphic hierarchy (one with
at least one virtual function) and checks at runtime that the object really is the target type.
On a pointer it returns nullptr if the cast is invalid; on a reference it throws
std::bad_cast. Use it when you have a base pointer and genuinely need to ask “is this actually
a Derived?” It costs a runtime lookup, which is the price of safety.
const_cast, add or remove const
const_cast<T>(v) is the only cast that can strip (or add) const/volatile.
Its honest use is narrow: calling a legacy API that isn’t const-correct but won’t actually modify
your object. Actually writing through a pointer to an originally-const object is undefined behavior.
Reach for it rarely, and treat its presence as a smell worth a comment.
reinterpret_cast, reinterpret the bits
reinterpret_cast<T>(v) tells the compiler “treat these bits as this other type” with
essentially no conversion, pointer-to-integer, or between unrelated pointer types. It is the bluntest, least
portable cast and the easiest to get wrong (alignment, aliasing, endianness). Confine it to low-level code that
truly needs it, like hardware interfaces or serialization.
Which one, in one line each
- Ordinary, checkable conversion → static_cast.
- Safe downcast in a polymorphic hierarchy → dynamic_cast.
- Add/remove const for an API → const_cast (sparingly).
- Reinterpret raw bits → reinterpret_cast (rarely, carefully).
FAQ
Why avoid C-style casts?
A single (Type)x may silently perform any of the four conversions, including a dangerous reinterpret, with no signal of intent and no way to grep for the risky ones. The named casts make intent explicit and reviewable.
When does dynamic_cast return null vs throw?
On a pointer a failed cast returns nullptr; on a reference (which can’t be null) it throws std::bad_cast.
Does dynamic_cast work on any class?
Only polymorphic ones, the type needs at least one virtual function so the runtime has type information to check against.
Related
See smart pointers & RAII and memory leaks, or the full Reference.