Working With Data
Type Casting in C++
Table of Contents
Introduction to Type Casting
Type casting (or type conversion) changes a value from one type to another. C++ provides four cast operators for explicit conversions.
Cast Operators Overview
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β C++ CAST OPERATORS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββ βββββββββββββββββββ β
β β static_cast β β dynamic_cast β β
β β β β β β
β β β’ Compile-time β β β’ Runtime β β
β β β’ No RTTI β β β’ Uses RTTI β β
β β β’ Fast β β β’ Polymorphic β β
β β β’ Most common β β β’ Safe β β
β βββββββββββββββββββ βββββββββββββββββββ β
β β
β βββββββββββββββββββ βββββββββββββββββββ β
β β const_cast β β reinterpret_castβ β
β β β β β β
β β β’ const/volatileβ β β’ Bit pattern β β
β β β’ Add or remove β β β’ Dangerous β β
β β β’ Use carefully β β β’ Low-level β β
β β β’ API compat β β β’ Non-portable β β
β βββββββββββββββββββ βββββββββββββββββββ β
β β
β Use hierarchy: β
β static_cast > dynamic_cast > const_cast > reinterpret_cast β
β (most preferred) (least preferred) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
C-Style Casts
Traditional C-style casts are powerful but dangerous.
// C-style cast syntax
int x = (int)3.14; // Explicit cast
int y = int(3.14); // Function-style cast
// What C-style casts actually do:
double d = 3.14;
int i = (int)d; // Tries: static_cast, reinterpret_cast, const_cast
// in that order until one works
β οΈ Problems with C-Style Casts
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WHY AVOID C-STYLE CASTS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. INVISIBLE INTENT β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β (int)ptr // Is this intentional pointer-to-int cast? β β
β β // Or is it a mistake? β β
β β // Hard to search for in codebase β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β 2. TOO POWERFUL - Does multiple things silently β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β const int* cp = &x; β β
β β int* p = (int*)cp; // Removes const AND casts type β β
β β // Would need const_cast in C++ β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β 3. NO COMPILE-TIME CHECKS β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β class Base {}; β β
β β class Derived : public Base {}; β β
β β class Unrelated {}; β β
β β β β
β β Base* b = new Derived(); β β
β β Unrelated* u = (Unrelated*)b; // Compiles! Undefined behavior! β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
static_cast
The most commonly used cast. Performs compile-time type checking.
Use Cases
// 1. Numeric conversions
double d = 3.14159;
int i = static_cast<int>(d); // 3 (truncates)
float f = static_cast<float>(10) / 3; // 3.333... (avoids integer division)
// 2. Pointer conversions in class hierarchy
class Base {};
class Derived : public Base {};
Derived* d = new Derived();
Base* b = static_cast<Base*>(d); // Upcast (safe, implicit anyway)
Derived* d2 = static_cast<Derived*>(b); // Downcast (unsafe, no runtime check!)
// 3. void* to typed pointer
void* vp = malloc(sizeof(int));
int* ip = static_cast<int*>(vp); // Convert void* to int*
// 4. Enum to int and vice versa
enum class Color { Red, Green, Blue };
int colorValue = static_cast<int>(Color::Green); // 1
Color c = static_cast<Color>(2); // Color::Blue
// 5. Explicit constructor calls
class MyClass {
public:
explicit MyClass(int x) {}
};
MyClass obj = static_cast<MyClass>(42); // Calls constructor
static_cast Visualization
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β static_cast USE CASES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β β
SAFE CONVERSIONS: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β int β double (widening) β β
β β float β int (narrowing, may lose precision) β β
β β Derived* β Base* (upcast) β β
β β enum class β int (underlying type) β β
β β void* β T* (when you know the actual type) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β β οΈ POTENTIALLY UNSAFE (but compiles): β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Base* β Derived* (downcast - no runtime check!) β β
β β Use dynamic_cast for safety β β
β β double β int (loses fractional part) β β
β β int β short (may overflow) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β β WILL NOT COMPILE: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β int* β double* (unrelated pointer types) β β
β β const int* β int* (removing const - use const_cast) β β
β β int β void* (use reinterpret_cast) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
dynamic_cast
Runtime type-safe casting for polymorphic types (classes with virtual functions).
How It Works
class Base {
public:
virtual ~Base() = default; // Must have virtual function for dynamic_cast
};
class Derived : public Base {
public:
void derivedMethod() { cout << "Derived method" << endl; }
};
class AnotherDerived : public Base {};
// Downcast with runtime check
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b);
if (d != nullptr) {
d->derivedMethod(); // Safe!
} else {
cout << "Cast failed" << endl;
}
// Reference version throws on failure
try {
Derived& dRef = dynamic_cast<Derived&>(*b);
dRef.derivedMethod();
} catch (const std::bad_cast& e) {
cout << "Cast failed: " << e.what() << endl;
}
dynamic_cast Behavior
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β dynamic_cast BEHAVIOR β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Given: β
β βββββββββββββββ β
β β Base β ββββ Has virtual function β
β ββββββββ¬βββββββ β
β β β
β ββββββ΄βββββ β
β β β β
β βββ΄βββ ββββ΄ββ β
β β D1 β β D2 β β
β ββββββ ββββββ β
β β
β Base* b = new D1(); β
β β
β POINTER CASTS: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β dynamic_cast<D1*>(b) β Valid D1* pointer β β β
β β dynamic_cast<D2*>(b) β nullptr β (wrong type) β β
β β dynamic_cast<Base*>(b) β Valid Base* pointer β (upcast) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β REFERENCE CASTS: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β dynamic_cast<D1&>(*b) β Valid D1& reference β β β
β β dynamic_cast<D2&>(*b) β throws std::bad_cast β β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β REQUIREMENTS: β
β β’ Source type must be polymorphic (have virtual functions) β
β β’ Uses RTTI (Runtime Type Information) β
β β’ Has runtime overhead β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Cross-Cast (Sibling Cast)
class Base { virtual ~Base() = default; };
class D1 : public Base {};
class D2 : public Base {};
class Multi : public D1, public D2 {};
Multi* m = new Multi();
D1* d1 = m;
// Cross-cast from D1 to D2 (through Multi)
D2* d2 = dynamic_cast<D2*>(d1); // Works! Finds Multi, then D2
const_cast
Adds or removes const or volatile qualifiers.
Use Cases
// 1. Remove const (use carefully!)
const int x = 42;
int* p = const_cast<int*>(&x);
// *p = 100; // UNDEFINED BEHAVIOR if x was truly const!
// 2. Safe use: calling non-const API with const data
void legacyAPI(char* str); // Old API doesn't take const
void modernFunction(const char* str) {
// We know legacyAPI won't modify str
legacyAPI(const_cast<char*>(str));
}
// 3. In const member function, modify mutable-like members
class Cache {
mutable int cachedValue;
bool cacheValid;
public:
int getValue() const {
if (!cacheValid) {
// Need to modify cache in const function
Cache* self = const_cast<Cache*>(this);
self->cachedValue = computeValue();
self->cacheValid = true;
}
return cachedValue;
}
};
// 4. Avoid code duplication (const/non-const overloads)
class Container {
int data[100];
public:
int& at(int i) {
// Non-const version delegates to const version
return const_cast<int&>(
static_cast<const Container*>(this)->at(i)
);
}
const int& at(int i) const {
// Actual implementation
return data[i];
}
};
const_cast Rules
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β const_cast RULES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β β
VALID USES: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β const T* β T* (add/remove const from pointer) β β
β β const T& β T& (remove const from reference) β β
β β volatile T* β T* (add/remove volatile) β β
β β const volatile T* β T* (add/remove both) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β β οΈ UNDEFINED BEHAVIOR: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β const int x = 42; β β
β β int* p = const_cast<int*>(&x); β β
β β *p = 100; // UB! x was declared const β β
β β β β
β β // Only safe if original object was NOT const: β β
β β int y = 42; β β
β β const int* cp = &y; β β
β β int* p2 = const_cast<int*>(cp); β β
β β *p2 = 100; // OK! y was not originally const β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β β CANNOT DO: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β const_cast<int>(3.14) // Not for value conversion β β
β β const_cast<double*>(ip) // Not for type conversion β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
reinterpret_cast
Low-level bit reinterpretation. Treats memory as a different type.
Use Cases
// 1. Pointer to integer and back
int* p = new int(42);
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
cout << "Address: " << hex << addr << endl;
int* p2 = reinterpret_cast<int*>(addr);
// 2. Between pointer types (dangerous!)
struct NetworkPacket {
uint32_t header;
uint8_t data[100];
};
char buffer[104];
receiveData(buffer);
NetworkPacket* packet = reinterpret_cast<NetworkPacket*>(buffer);
// 3. Function pointer casts
using FuncPtr = void(*)();
void* libHandle = dlopen("lib.so", RTLD_NOW);
void* sym = dlsym(libHandle, "myFunction");
FuncPtr func = reinterpret_cast<FuncPtr>(sym);
func();
// 4. Type punning (better to use memcpy or std::bit_cast)
float f = 3.14f;
// Old way (UB according to strict aliasing):
// uint32_t bits = *reinterpret_cast<uint32_t*>(&f);
// Better (C++20):
// uint32_t bits = std::bit_cast<uint32_t>(f);
reinterpret_cast Dangers
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β reinterpret_cast DANGERS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β β οΈ STRICT ALIASING VIOLATION: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β float f = 1.0f; β β
β β int* pi = reinterpret_cast<int*>(&f); β β
β β int i = *pi; // UNDEFINED BEHAVIOR! β β
β β // Compiler may optimize incorrectly β β
β β β β
β β // Safe alternatives: β β
β β int i; β β
β β memcpy(&i, &f, sizeof(i)); // C++11 β β
β β // OR β β
β β int i = std::bit_cast<int>(f); // C++20 β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β β οΈ ALIGNMENT ISSUES: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β char buffer[5]; β β
β β int* p = reinterpret_cast<int*>(buffer + 1); β β
β β *p = 42; // May crash on architectures requiring aligned access! β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β β οΈ POINTER SIZE MISMATCH: β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β void* p = ...; β β
β β int addr = reinterpret_cast<int>(p); // Truncates on 64-bit! β β
β β // Use uintptr_t instead of int β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β WHEN TO USE: β
β β’ Low-level I/O, hardware access β
β β’ Serialization/deserialization β
β β’ Interfacing with C libraries β
β β’ NEVER for normal application code β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Comparison & Recommended Conventions
Cast Selection Flowchart
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WHICH CAST SHOULD I USE? β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Start: What are you trying to do? β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Numeric conversion (intβdoubleβfloat)? β β
β β β static_cast β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Upcast in class hierarchy (Derived* β Base*)? β β
β β β Implicit cast (no cast needed) OR static_cast β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Downcast in class hierarchy (Base* β Derived*)? β β
β β β Is the class polymorphic (has virtual functions)? β β
β β YES β dynamic_cast (runtime check, safe) β β
β β NO β static_cast (no check, you must be sure!) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Add or remove const/volatile? β β
β β β const_cast β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Convert between unrelated pointer types? β β
β β Convert pointer to integer or vice versa? β β
β β Low-level bit manipulation? β β
β β β reinterpret_cast (last resort!) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Type punning (view bits of one type as another)? β β
β β β std::bit_cast (C++20) or memcpy β β
β β β NOT reinterpret_cast (strict aliasing violation) β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Cheat Sheet Table
| Cast | Compile/Runtime | Safe | Use Case |
|---|---|---|---|
static_cast |
Compile | Mostly | Numeric, void*, upcast, explicit downcast |
dynamic_cast |
Runtime | Yes | Safe polymorphic downcast |
const_cast |
Compile | Careful | Add/remove const/volatile |
reinterpret_cast |
Compile | No | Low-level bit manipulation |
| C-style | Compile | No | AVOID |
Recommended Conventions
// β
DO:
// 1. Use static_cast for numeric conversions
double d = 3.14;
int i = static_cast<int>(d);
// 2. Use dynamic_cast for safe downcasting
if (Derived* d = dynamic_cast<Derived*>(basePtr)) {
d->derivedMethod();
}
// 3. Use const_cast only when necessary
legacyFunction(const_cast<char*>(constString));
// 4. Minimize use of reinterpret_cast
// Only for hardware/serialization needs
// β DON'T:
// 1. Don't use C-style casts
int* p = (int*)voidPtr; // Bad
int* p = static_cast<int*>(voidPtr); // Good
// 2. Don't downcast without checking
Derived* d = static_cast<Derived*>(basePtr); // Risky
// Use dynamic_cast or be 100% sure of the type
// 3. Don't remove const from truly const objects
const int x = 42;
int* p = const_cast<int*>(&x);
*p = 100; // UNDEFINED BEHAVIOR!
Cheat Sheet
// static_cast - compile-time, most common
static_cast<NewType>(expression)
// dynamic_cast - runtime, polymorphic types
dynamic_cast<NewType*>(pointer) // Returns nullptr on failure
dynamic_cast<NewType&>(reference) // Throws std::bad_cast on failure
// const_cast - add/remove const or volatile
const_cast<NonConstType*>(constPointer)
// reinterpret_cast - bit reinterpretation
reinterpret_cast<DestType>(expression)
// C++20 bit_cast - type-safe bit reinterpretation
std::bit_cast<DestType>(source)
Compile & Run
g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
./examples
# Enable RTTI (needed for dynamic_cast)
g++ -std=c++17 -frtti examples.cpp -o examples
# Disable RTTI (dynamic_cast won't work)
g++ -std=c++17 -fno-rtti examples.cpp -o examples