This is a quick post about the 4 explicit casts in modern C++ to replace the type cast in C++ (inherited from C).
static_cast Link to heading
This is used for basic type conversions and up-casting in class hierarchies (although implicit up-cast works).
// Basic type conversion
int i = 42;
double d = static_cast<double>(i);
// Pointer conversion in inheritance
class Base {};
class Derived : public Base {};
Derived* derived = new Derived();
Base* base = static_cast<Base*>(derived); // Upcast - safe
Use static_cast
when:
- Converting between numeric types
- Upcasting in inheritance hierarchies
- Converting void* to typed pointers
const_cast Link to heading
Used to change (remove or add) const to a variable.
void modify_data(int* data) {
*data = 100;
}
// Remove const to pass to function
const int value = 42;
modify_data(const_cast<int*>(&value));
// More common use case - removing const from this to use in const method
class MyClass {
int cache_;
public:
int expensive_calculation() const {
// Remove const to modify mutable member
const_cast<MyClass*>(this)->cache_ = 42;
return cache_;
}
};
Use const_cast
when:
- Interfacing with legacy APIs that don’t use const correctly
- Implementing const and non-const versions of the same function
- Working with mutable members in const functions
dynamic_cast Link to heading
Used for safe downcasting in inheritance hierarchies. Returns nullptr for pointers or throws std::bad_cast for references if the cast fails.
class Base {
public:
virtual ~Base() {} // Polymorphic base class
};
class Derived : public Base {};
Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base); // Safe downcast
if (derived) {
std::cout << "Successful downcast\n";
} else {
std::cout << "Failed downcast\n";
}
Use dynamic_cast
when:
- Performing runtime type checking
- Safely downcasting in polymorphic hierarchies
- You need to verify the actual type at runtime
reinterpret_cast Link to heading
The documentation says “Converts between types by reinterpreting the underlying bit pattern,” which I don’t fully understand, but these seem to be the use cases for it:
- Converting between unrelated pointer types
- Converting pointers to integers and vice versa
- Low-level memory manipulation
- Interfacing with C APIs or hardware
// Converting between pointer types
int* int_ptr = new int(42);
char* char_ptr = reinterpret_cast<char*>(int_ptr);
// Converting pointers to integers
uintptr_t address = reinterpret_cast<uintptr_t>(int_ptr);
// Converting function pointers
void (*func_ptr)() = reinterpret_cast<void(*)()>(address);