16 / 163 · C++11 · 10 min
The Four Casts: Conversion Intent and Safety Preconditions
static_cast, dynamic_cast, const_cast, and reinterpret_cast each express a different conversion intent, but the keywords themselves do not guarantee runtime safety. Before choosing one, first establish numeric range, dynamic type, actual mutability, and the object's lifetime, alignment, and type-access rules.
In this lesson
Numeric Conversions and Type Hierarchies
static_cast is used for clearly allowed numeric conversions, certain class-hierarchy conversions, and other conversions that can be described statically. It does not automatically check whether a value is out of range; a floating-to-integer conversion should first ensure the truncated value is representable, and a downcast must actually point to the corresponding derived object rather than rest on the programmer's guess.
dynamic_cast performs the required runtime check in a polymorphic class hierarchy and is appropriate for downcasts or cross-casts whose dynamic type is uncertain. The pointer form returns a null pointer on failure; the reference form throws std::bad_cast on failure. It still requires a valid input object in an appropriate lifetime and cannot be used to validate an arbitrary corrupted address.
Qualification Conversion and Low-Level Interpretation
const_cast changes const or volatile qualification; it does not change whether the actual object was originally modifiable. The example restores writable access from a read-only pointer to an ordinary int, so the write is legitimate; if the original object was defined as const, writing after the conversion is undefined behavior.
reinterpret_cast expresses a low-level or pointer-level reinterpretation, but it does not automatically create a target object, satisfy alignment, or lift type-access restrictions. Converting a float* to an int* and then reading is not a general way to inspect a bit pattern; memcpy is appropriate in suitable cases, and C++20 also provides bit_cast constrained by type and size.
Make Dangerous Preconditions Reviewable
C-style cast syntax is short, yet it may combine several conversion abilities, making it hard for a reader to see whether const was removed or a low-level reinterpretation occurred. New code should prefer named casts so the dangerous points become searchable; even better designs often need no cast at all, for example by using the correct interface type or a virtual function.
The example verifies successful and failed dynamic conversions, a numeric conversion known to be in a safe range, and a qualification conversion where the original object is truly non-const. All preconditions are visible in nearby code; incorrect conversions are not executed in order to "observe the result". An interview answer should also state preconditions and failure behavior, not merely list the four English names.
Pitfalls
- static_cast from a base class to a derived class does not perform a runtime type check; the fact that the conversion compiles is not proof that the object type is correct.
- A successful reinterpret_cast that produces some pointer value does not mean you may dereference through that type; lifetime, alignment, and type accessibility must each hold.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <iostream>
struct Base { virtual ~Base() = default; };
struct Derived : Base { int value = 7; };
struct Other : Base {};
int main() {
Derived object;
Base* base = &object;
Derived* derived = dynamic_cast<Derived*>(base);
assert(derived != nullptr && derived->value == 7);
assert(dynamic_cast<Other*>(base) == nullptr);
int editable = 3;
const int* read_only = &editable;
*const_cast<int*>(read_only) = 4;
const int whole = static_cast<int>(3.75);
assert(editable == 4 && whole == 3);
std::cout << derived->value << ' ' << editable << ' ' << whole << '\n';
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread basics-casts.cpp -o example && ./exampleExpected result
7 4 3
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
const int fixed = 5; const int* p = &fixed;. Can const_cast<int*>(p) be formed? Can you write 6 through the result? What happens if dynamic_cast<Derived&> fails instead?
Show a reference answer
The pointer with the qualification removed can be formed, but writing 6 is illegal because the actual object has been const since its definition, and modifying it is undefined behavior. A well-formed conversion expression and a well-formed later access are two different questions. The reference form of dynamic_cast has no null reference as a failure value, so a failed check throws std::bad_cast; catch that exception, or choose the pointer form when failure is allowed.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.