Introduction
In C++, four different cast operators are provided to perform explicit type conversion (casting), such as converting an int to a double, or a parent class pointer to a child class pointer. These are safer than the old C-style cast (type)value because their purpose is clearer and compiler checks are stricter.
In this article, I will explain the roles and correct usage of the four main cast operators:
static_cast: Static type conversiondynamic_cast: Dynamic type conversion (Safe downcast)const_cast: Changingconst/volatilequalifiersreinterpret_cast: The most dangerous, bit-level reinterpretation
1. static_cast
static_cast is used for the most common conversions where safety can be determined at compile time, such as conversions between numeric types or pointer/reference conversions between classes in an inheritance relationship (upcast/downcast).
#include <iostream>
struct Base {};
struct Derived : Base {};
int main() {
// 1. Conversion between numeric types
double pi = 3.14;
int integer_pi = static_cast<int>(pi); // double -> int
std::cout << "Converted to int: " << integer_pi << std::endl;
// 2. Pointer conversion between inheritance relationships
Derived d;
// Upcast (Child -> Parent) is safe, so static_cast is OK
Base* p_base = static_cast<Base*>(&d);
// Downcast (Parent -> Child) is dangerous but allowed at the programmer's risk
Derived* p_derived = static_cast<Derived*>(p_base);
return 0;
}
Note: Downcasting with static_cast (Parent pointer -> Child pointer) does not check at runtime whether the pointer actually points to an object of that child class. If it is wrong, it causes undefined behavior.
2. dynamic_cast
dynamic_cast performs pointer/reference conversions between classes in an inheritance relationship while checking type safety at runtime. It is mainly used for safe downcasting.
#include <iostream>
// Must have virtual functions to enable RTTI
struct Base { virtual ~Base() {} };
struct Derived : Base {};
struct Another : Base {};
int main() {
Derived d;
Base* p_base = &d;
// 1. Successful downcast
if (Derived* p_derived = dynamic_cast<Derived*>(p_base)) {
std::cout << "Downcast to Derived succeeded" << std::endl;
}
// 2. Failed downcast
if (Another* p_another = dynamic_cast<Another*>(p_base)) {
// This block is not executed
} else {
std::cout << "Downcast to Another failed (Result is nullptr)" << std::endl;
}
return 0;
}
Explanation:
dynamic_castcan only be used on polymorphic classes that have at least one virtual function.- If the downcast succeeds, it returns a valid pointer.
- If it fails, it returns
nullptrfor pointers, or throws astd::bad_castexception for references.
3. const_cast
const_cast is a special cast used solely to add or remove const or volatile qualifiers.
void print_message(char* str) { std::cout << str << std::endl; }
int main() {
const char* message = "Hello";
// print_message requires a non-const pointer
// Use const_cast to remove const
print_message(const_cast<char*>(message));
return 0;
}
Warning: Using const_cast to remove const from an object that was originally defined as const and then attempting to modify its value is undefined behavior and very dangerous.
4. reinterpret_cast
reinterpret_cast is the most dangerous cast. It forcibly reinterprets type information while keeping the bit pattern exactly the same, such as converting a pointer type to a completely unrelated pointer type.
#include <cstdint>
#include <iostream>
int main() {
long long value = 0x41424344; // "DCBA" in ASCII
// Reinterpret the address of long long as a pointer to char
char* p_char = reinterpret_cast<char*>(&value);
// Output depends on the environment (endianness), but the first byte ("D") might be output
std::cout << *p_char << std::endl;
return 0;
}
Note: This cast should only be used in extremely limited situations, such as low-level interactions with the OS.
Summary
| Cast | Main Usage | Safety |
| static_cast | General type conversion, safe upcast | Compile-time check |
| dynamic_cast | Safe downcast | Runtime check |
| const_cast | Changing const/volatile qualifiers | Low (Caution required) |
| reinterpret_cast | Bit-level reinterpretation | Extremely Low (Avoid in principle) |
In C++, using these four casts appropriately clarifies the intent of the type conversion and increases code safety.
