Reference · C++

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 (doubleint), 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.