Question
When to Use static_cast, dynamic_cast, const_cast, and reinterpret_cast in C++
Question
In C++, what are the correct use cases for these casting forms?
static_castdynamic_castconst_castreinterpret_cast(type)value(C-style cast)type(value)(function-style cast)
How do you decide which cast to use in specific situations, and what are the differences in safety and intent between them?
Short Answer
By the end of this page, you will understand what each C++ cast is designed for, when it is appropriate to use it, and which casts are risky or should usually be avoided. You will also learn how to choose the safest cast for common programming situations, especially when converting between numeric types, working with inheritance, removing const, or dealing with low-level memory operations.
Concept
C++ provides several different cast operators because not all type conversions mean the same thing.
In older C-style code, a cast like (type)value could do many different things at once. That makes code shorter, but also less clear and less safe. C++ introduced named casts so the programmer's intent is explicit.
Here is the big idea:
static_castis for normal, compile-time conversions.dynamic_castis for safe casting in inheritance hierarchies at runtime.const_castis for adding or removingconstorvolatilequalifiers.reinterpret_castis for low-level bit-pattern reinterpretation.- C-style and function-style casts are less explicit and may perform one of several kinds of conversions.
Why this matters:
- It makes code easier to read.
- It makes unsafe operations easier to spot.
- It helps the compiler catch mistakes.
- It communicates whether a conversion is routine, runtime-checked, qualifier-related, or low-level.
A useful rule of thumb is:
- Avoid casting if you can. Prefer designing code so conversion is unnecessary.
- If you must cast, use the most specific named cast.
- Avoid C-style casts in modern C++ because they can hide dangerous conversions.
Mental Model
Think of C++ casts as different kinds of permission slips:
static_cast= standard office form. Normal, expected conversions.dynamic_cast= security checkpoint. It checks identity before allowing entry.const_cast= removing a “do not modify” sticker. Dangerous if the object was truly meant to stay unchanged.reinterpret_cast= labeling one box as if it were a completely different box. Sometimes necessary at low level, but very risky.- C-style cast = a mystery shortcut. It might do several things, and readers cannot easily tell which one.
Another way to remember it:
static_cast: “Convert this in a normal way.”dynamic_cast: “Check what this object really is at runtime.”const_cast: “Change constness only.”reinterpret_cast: “Treat these bits/address as another type.”
The more specific the cast, the clearer your code is.
Syntax and Examples
Core syntax
static_cast<NewType>(value)
dynamic_cast<NewType>(value)
const_cast<NewType>(value)
reinterpret_cast<NewType>(value)
Older forms:
(NewType)value
NewType(value)
static_cast example
#include <iostream>
int main() {
double temperature = 23.7;
int roundedDown = static_cast<int>(temperature);
std::cout << roundedDown << '\n';
}
static_cast<int>(temperature) converts 23.7 to 23. This is a normal numeric conversion.
dynamic_cast example
{
:
~() = ;
};
: Shape {
:
{
std::cout << ;
}
};
{
Shape* shape = ();
Circle* circle = <Circle*>(shape);
(circle) {
circle->();
}
shape;
}
Step by Step Execution
Consider this example:
#include <iostream>
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
void hello() {
std::cout << "Hello from Derived\n";
}
};
int main() {
Base* ptr = new Derived();
Derived* d1 = static_cast<Derived*>(ptr);
d1->hello();
Derived* d2 = dynamic_cast<Derived*>(ptr);
if (d2) {
d2->hello();
}
delete ptr;
}
What happens step by step
-
Base* ptr = new Derived();- A
Derivedobject is created. - It is stored in a pointer of type
Base*. - This is an , and it is safe.
- A
Real World Use Cases
Numeric and data conversions
static_cast is commonly used when:
- converting
doubletoint - converting enum values to integers
- converting sizes between numeric types intentionally
double total = 42.8;
int whole = static_cast<int>(total);
Object-oriented code with inheritance
dynamic_cast is useful when working with plugin systems, UI widgets, AST nodes, or game objects where a base pointer may actually refer to different derived types.
Widget* widget = getSelectedWidget();
Button* button = dynamic_cast<Button*>(widget);
if (button) {
button->click();
}
Interfacing with legacy APIs
const_cast may be needed when an old library takes a non-const pointer even though it does not modify the data.
legacy_api(const_cast<char*>(name.c_str()));
Real Codebase Usage
In real C++ codebases, casts often appear as part of defensive patterns.
1. Prefer no cast when possible
Developers often redesign APIs so callers do not need casts.
Instead of this:
void process(Base* obj);
You may see virtual functions used so the correct behavior is chosen automatically.
2. static_cast for explicit intent
When narrowing or converting intentionally, developers use static_cast to show that data loss or transformation is expected.
int size = static_cast<int>(items.size());
This is common when an API expects a specific numeric type.
3. dynamic_cast for safe downcasting at boundaries
In large codebases, dynamic_cast is often used at framework boundaries where a generic interface returns a base pointer, and code must verify the exact subtype.
if (auto* fileLogger = dynamic_cast<FileLogger*>(logger)) {
fileLogger->();
}
Common Mistakes
1. Using static_cast for unsafe downcasting
Broken example:
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
void hello() {}
};
Base base;
Base* ptr = &base;
Derived* d = static_cast<Derived*>(ptr); // dangerous
This compiles, but ptr does not actually point to a Derived. Using d as a Derived* is undefined behavior.
Use dynamic_cast when you need runtime checking.
2. Removing const and then modifying truly const data
Broken example:
const int value = 10;
int* p = <*>(&value);
*p = ;
Comparisons
Cast comparison table
| Cast | Main purpose | Runtime check | Safety level | Typical use |
|---|---|---|---|---|
static_cast | Normal conversions | No | Medium | Numeric conversions, upcasts, trusted downcasts |
dynamic_cast | Safe downcasting in polymorphism | Yes | Higher | Checking actual derived type |
const_cast | Change const/volatile qualifiers | No | Low to medium | Legacy APIs, qualifier adjustment |
reinterpret_cast |
Cheat Sheet
Quick rules
- Best rule: avoid casting unless necessary.
- Prefer named casts over C-style casts.
- Use the most specific cast for your intent.
Which cast should I use?
- Normal conversion? ->
static_cast - Need runtime type check in inheritance? ->
dynamic_cast - Only changing
constorvolatile? ->const_cast - Low-level bit/address reinterpretation? ->
reinterpret_cast - Old
(type)valuecast? -> usually replace with a named cast
Syntax
static_cast<T>(value)
dynamic_cast<T>(value)
const_cast<T>(value)
reinterpret_cast<T>(value)
Safety notes
-
static_cast- good for explicit numeric conversions
- no runtime safety for downcasts
-
dynamic_cast
FAQ
What is the safest cast in C++?
There is no single safest cast for every case, but static_cast is the usual safe choice for normal explicit conversions, and dynamic_cast is safest for uncertain downcasts in polymorphic class hierarchies.
Why are C-style casts discouraged in C++?
Because they are ambiguous. A C-style cast can perform several different kinds of conversions, making code harder to read and easier to misuse.
When should I use dynamic_cast instead of static_cast?
Use dynamic_cast when converting from a base type to a derived type and the real object type may vary at runtime.
Can const_cast remove const safely?
It can remove the qualifier syntactically, but modifying an object that was originally declared const is undefined behavior.
Is reinterpret_cast portable?
Often not fully. It is closely tied to platform details, memory layout, and representation rules, so it should be used carefully and sparingly.
Does dynamic_cast require virtual functions?
Yes, the base class must be polymorphic, which usually means it has at least one virtual function such as a virtual destructor.
Mini Project
Description
Build a small C++ program that demonstrates the four main named casts in realistic situations. The project uses numeric conversion, safe downcasting, qualifier adjustment for a legacy function, and a low-level pointer reinterpretation example. The goal is not to encourage unnecessary casting, but to help you recognize when each cast is intended to be used.
Goal
Create a program that prints examples of static_cast, dynamic_cast, const_cast, and reinterpret_cast, and clearly shows which operations are safe and which require caution.
Requirements
- Create one example that converts a
doubleto anintusingstatic_cast. - Create a polymorphic base class and safely downcast with
dynamic_cast. - Call a legacy-style function using
const_castwithout modifying the original data. - Show a
reinterpret_castexample that converts a pointer to an integer address and prints it. - Print clear output labels so each cast example is easy to identify.
Keep learning
Related questions
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Casts Explained: C-Style Cast vs static_cast vs dynamic_cast
Learn the difference between C-style casts, static_cast, and dynamic_cast in C++ with clear examples, safety rules, and real usage tips.
C++ Lambda Expressions Explained: What They Are and When to Use Them
Learn what C++ lambda expressions are, why they exist, when to use them, and how they simplify callbacks, algorithms, and local logic.