Casting

From Claude 3.5 Sonnet.

C++ Special Casts Explained

C++ provides several special cast operators to handle different types of type conversions safely and explicitly. These casts are:

  1. static_cast

  2. dynamic_cast

  3. const_cast

  4. reinterpret_cast

Let's explore each of these in detail.

1. static_cast

static_cast is used for conversions between related types, such as numeric types or up and down a class hierarchy.

Usage:

static_cast<new_type>(expression)

Examples:

// Convert int to float
int i = 10;
float f = static_cast<float>(i);

// Upcast (derived class to base class)
class Base {};
class Derived : public Base {};
Derived d;
Base* b = static_cast<Base*>(&d);

// Downcast (base class to derived class) - unsafe, use dynamic_cast for this
Base* b = new Derived();
Derived* d = static_cast<Derived*>(b); // This works but can be dangerous

2. dynamic_cast

dynamic_cast is used for safe downcasting in inheritance hierarchies. It performs a runtime check and returns nullptr (for pointers) or throws an exception (for references) if the cast is not valid.

Usage:

dynamic_cast<new_type>(expression)

Example:

class Base { virtual void dummy() {} };  // Must have at least one virtual function
class Derived : public Base { };

Base* b1 = new Base();
Base* b2 = new Derived();

Derived* d1 = dynamic_cast<Derived*>(b1);  // Returns nullptr
Derived* d2 = dynamic_cast<Derived*>(b2);  // Returns valid pointer

if (d1) {
    std::cout << "Cast successful" << std::endl;
} else {
    std::cout << "Cast failed" << std::endl;
}

3. const_cast

const_cast is used to add or remove const (or volatile) qualifiers from a variable.

Usage:

const_cast<new_type>(expression)

Example:

const int* ptr = new int(10);
int* mutable_ptr = const_cast<int*>(ptr);
*mutable_ptr = 20;  // Modifies the original data

// Note: Modifying a value that was originally declared const 
// leads to undefined behavior

4. reinterpret_cast

reinterpret_cast is used for low-level reinterpreting of bit patterns. It's the most dangerous cast and should be used sparingly.

Usage:

reinterpret_cast<new_type>(expression)

Example:

int* p = new int(42);
char* ch = reinterpret_cast<char*>(p);

std::cout << *p << std::endl;  // Prints 42
std::cout << *ch << std::endl; // Undefined behavior

// Convert between unrelated pointer types
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
int* p2 = reinterpret_cast<int*>(addr);

Best Practices

  1. Use static_cast for most conversions between related types.

  2. Use dynamic_cast for safe downcasting in polymorphic class hierarchies.

  3. Use const_cast only when you're certain that the original variable wasn't const.

  4. Use reinterpret_cast only in low-level code where you need to reinterpret memory.

  5. Avoid C-style casts (new_type)expression as they can hide dangerous conversions.

Remember, explicit casts should be used judiciously. If you find yourself using many casts, especially reinterpret_cast or const_cast, it might indicate a design problem in your code.

Last updated