CppType Casting

Type Casting

Type casting converts a value from one type to another. C++ performs some conversions automatically (implicit conversions) and lets you request others explicitly (casts). Understanding the difference — and which cast to reach for — is essential for writing safe, predictable code.

Implicit Conversions
The compiler automatically converts between compatible types when it is safe and lossless (or at least well understood), such as widening an int into a double.

CPP
int wholeNumber = 5;
double result = wholeNumber; // implicit conversion: int -> double, result is 5.0

double pi = 3.14;
int truncated = pi;          // implicit conversion: double -> int, truncated is 3 (data loss!)
C-Style Casts
Inherited from C, the C-style cast looks like (int)x. It compiles down to whichever of static_cast, const_cast, or reinterpret_cast happens to apply — silently and without telling you which one it picked.

CPP
double price = 19.99;
int cents = (int)(price * 100); // C-style cast
Avoid C-style casts in C++
A C-style cast can silently combine static_cast, const_cast, and even reinterpret_cast behavior in one expression. It gives the compiler no way to warn you if you accidentally cast away const-ness or reinterpret unrelated pointer types. Modern C++ casts are narrower and safer precisely because each one only does one specific job.
Modern C++ Casts

Cast

Purpose

When to Use

static_cast<T>(x)

Well-defined conversions checked at compile time

Numeric conversions, upcasting in a class hierarchy, explicit conversions between related types

dynamic_cast<T>(x)

Safe downcasting in a polymorphic class hierarchy, checked at runtime

Downcasting a base pointer/reference to a derived type when you are not sure it will succeed

const_cast<T>(x)

Adds or removes const/volatile

Calling a legacy API that is not const-correct (rare, use sparingly)

reinterpret_cast<T>(x)

Reinterprets the raw bit pattern as a different, often unrelated, type

Low-level code: pointer/integer conversions, hardware or binary protocol work

Worked Example: static_cast for Numeric Conversion

CPP
#include <iostream>

int main() {
    double price = 19.99;

    // static_cast clearly documents intent: "I mean to convert this on purpose"
    int cents = static_cast<int>(price * 100);

    std::cout << cents << std::endl; // 1999
    return 0;
}
Worked Example: dynamic_cast for Safe Downcasting
dynamic_cast is the safe way to convert a base-class pointer or reference back to a derived type. It requires the base class to be **polymorphic** (have at least one virtual function) and checks at runtime whether the conversion is actually valid, returning nullptr (for pointers) if it is not. We cover polymorphism in depth on its own page — this is a preview of how the casts connect.

CPP
#include <iostream>

class Animal {
public:
    virtual ~Animal() = default; // must be polymorphic for dynamic_cast to work
};

class Dog : public Animal {
public:
    void bark() const { std::cout << "Woof!" << std::endl; }
};

int main() {
    Animal* animal = new Dog();

    // Safe downcast: returns nullptr if animal isn't actually a Dog
    if (Dog* dog = dynamic_cast<Dog*>(animal)) {
        dog->bark();
    } else {
        std::cout << "Not a Dog!" << std::endl;
    }

    delete animal;
    return 0;
}
Note
Prefer static_cast when you already know a conversion is valid (it has no runtime cost). Reach for dynamic_cast only when you genuinely need a runtime check, since it does incur a small performance cost.