Type Casting (static_cast & more)
Sometimes a value is the wrong type and you need to convert it deliberately. C++ gives you four named casts — static_cast , dynamic_cast , const_cast , and reinterpret_cast — each with a clear purpose. This lesson teaches you which to reach for, why they beat C-style (int) casts, and the truncation traps to watch for.
Learn Type Casting (static_cast & more) in our free C++ course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…
Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn
💡 Real-World Analogy
Think of the four casts as four different adapters . static_cast is a sensible travel adapter — it connects compatible plugs (an int into a double socket). dynamic_cast is a security guard who checks ID at the door and turns you away if you're not really who you claim to be. const_cast is a master key that removes a "do not touch" sticker — powerful but easy to misuse. reinterpret_cast is a crowbar that forces two unrelated things together by brute reinterpretation of their bits. You reach for the gentlest tool that does the job — almost always static_cast .
📊 The Four Named Casts
Cast
Use for
Checked?
static_cast
Related numeric/pointer conversions
Compile time
dynamic_cast
Safe downcast in a class hierarchy
Run time
const_cast
Add/remove const or volatile
No
reinterpret_cast
Reinterpret raw bits/pointers
1. static_cast : the workhorse
This is the cast you'll use 95% of the time. It converts between related types: int ↔ double , char ↔ its numeric code, and pointers within a hierarchy. The classic use is breaking the integer-division trap by casting one operand to double .
2. Truncation vs Rounding
Casting a double to int truncates toward zero — it chops the fraction, it does not round. If you want the nearest whole number, round first with std::round (or use std::lround , which returns an integer directly).
3. dynamic_cast : safe downcasting
When you have a base-class pointer and need to know the real derived type, dynamic_cast checks at runtime. It returns nullptr for pointers (or throws std::bad_cast for references) when the object isn't the type you asked for. It requires the base class to be polymorphic — at least one virtual function.
4. const_cast : removing const (carefully)
const_cast adds or removes const . Its main legitimate use is calling an older API that forgot to mark a read-only parameter const . Never use it to actually modify an object that is genuinely const — that's undefined behavior.
5. reinterpret_cast : raw bits (last resort)
This is the most dangerous cast. It views the same bytes through a completely different type — useful for low-level work like inspecting a value's individual bytes or treating a pointer as an integer address, but non-portable and easy to misuse. Reach for it only when nothing else works.
Your turn. Cast one operand so the percentage keeps its decimals:
🧩 Reorder Challenge
These lines round a price to the nearest cent-free whole number and print it. Reorder so the program prints 4 .
Why: #include <cmath> must precede any use of round . Inside main , declare price first, then round(3.6) gives 4.0 and static_cast<int> converts that cleanly to 4 (rounding before casting avoids truncating 3.6 down to 3 ). Then print and close the function.
🧠 Quick Recall
Predict the output before revealing each answer.
2 — casting a double to int truncates toward zero; it does not round to 3.
a — 97 is the ASCII code for lowercase 'a', so casting the int to char prints that character.
2.5 — casting one operand to double makes the whole division floating-point.
Pro Tips
- 💡 Default to static_cast : the other three are special-purpose tools.
- 💡 Round, don't truncate: wrap with round() when you want the nearest integer.
- 💡 dynamic_cast needs virtual : the base class must have at least one virtual function.
- 💡 Avoid C-style casts: (int)x hides which conversion is happening and can do something unexpected.
Common Errors (and the fix)
- Expecting rounding from a cast: static_cast<int>(2.9) is 2 . Use round() first.
- Casting too late: static_cast<double>(7 / 2) is 3.0 — the division already truncated. Cast an operand : static_cast<double>(7) / 2 .
- dynamic_cast on a non-polymorphic type: won't compile. Give the base a virtual destructor.
- Modifying through const_cast : writing to a truly const object is undefined behavior.
📋 Quick Reference
Cast / Idiom
Note
static_cast<T>(x)
related numeric/pointer conversions
the 95% cast; checked at compile time
dynamic_cast<T*>(p)
safe downcast in a class hierarchy
runtime check; nullptr on failure
const_cast<T>(x)
add / remove const
writing a true const is UB
reinterpret_cast<T>(x)
reinterpret raw bits / pointers
low-level, non-portable, last resort
static_cast<int>(3.9)
double → int
truncates toward zero → 3 (not 4)
static_cast<int>(round(x))
round then convert
round() / lround() from <cmath>
static_cast<double>(a) / b
force floating-point division
cast an operand, not the result
static_cast<int>('A')
char → ASCII code
'A' → 65
static_cast<char>(66)
code → char
66 → 'B'
dynamic_cast needs virtual
base must be polymorphic
at least one virtual function
(int)x
C-style cast
avoid — hides which conversion runs
Frequently Asked Questions
Mini-Challenge: Grade Calculator
Read points earned and possible, compute a rounded percentage with a cast, and map it to a letter grade. Match the expected output in the comments.
🎉 Lesson Complete
- ✅ static_cast handles related numeric and char conversions
- ✅ double → int truncates; round first for the nearest integer
- ✅ dynamic_cast safely downcasts polymorphic types at runtime
- ✅ const_cast strips const for legacy APIs (never to mutate a true const)
- ✅ reinterpret_cast reinterprets raw bits — a low-level last resort
- ✅ Next lesson: Enums — give names to sets of related constants
Practice quiz
What does static_cast<int>(3.9) evaluate to?
- 4
- 3.9
- 3
- 0
Answer: 3. Converting a double to int truncates toward zero — it drops the fraction rather than rounding, so 3.9 becomes 3.
What does static_cast<int>(-3.9) evaluate to?
- -3
- -4
- -3.9
- 3
Answer: -3. Truncation is toward zero, not toward negative infinity, so -3.9 becomes -3 (not -4).
Which cast should you reach for 95% of the time, for related numeric and char conversions?
- reinterpret_cast
- const_cast
- dynamic_cast
- static_cast
Answer: static_cast. static_cast handles well-defined, related conversions and is checked at compile time. It is the everyday workhorse cast.
Which cast is used for safe downcasting in a class hierarchy, checked at runtime?
- static_cast
- dynamic_cast
- const_cast
- reinterpret_cast
Answer: dynamic_cast. dynamic_cast checks the real type at runtime, returning nullptr for pointers (or throwing std::bad_cast for references) on failure.
What does dynamic_cast require of the base class to work?
- At least one virtual function (polymorphic)
- A copy constructor
- A const destructor
- Template parameters
Answer: At least one virtual function (polymorphic). dynamic_cast needs the base class to be polymorphic — at least one virtual function (commonly a virtual destructor).
What does static_cast<double>(7) / 2 evaluate to?
- 3
- 3.0
- 3.5
- 4
Answer: 3.5. Casting one operand to double makes the division floating-point: 7.0 / 2 = 3.5.
What does static_cast<double>(7 / 2) evaluate to?
- 3.5
- 3.0
- 4.0
- 2
Answer: 3.0. The integer division 7 / 2 happens first and truncates to 3; casting that to double gives 3.0. Cast an operand, not the result.
What does static_cast<char>(97) print?
- 97
- 9
- A
- a
Answer: a. 97 is the ASCII code for lowercase 'a', so casting the int to char yields that character.
What is the main legitimate use of const_cast?
- Speeding up loops
- Calling a legacy API that forgot to mark a parameter const
- Converting double to int
- Downcasting pointers
Answer: Calling a legacy API that forgot to mark a parameter const. const_cast strips const to satisfy older non-const APIs. Actually modifying a genuinely const object through it is undefined behavior.
Why prefer named C++ casts over a C-style cast like (int)x?
- They are shorter to type
- They run faster at runtime
- They make intent explicit and let the compiler reject nonsense
- They are required by the standard
Answer: They make intent explicit and let the compiler reject nonsense. A C-style cast silently tries const_cast, static_cast, even reinterpret_cast. Named casts state intent, are searchable, and reject invalid conversions.
Continue this course
- Previous: Strings (std::string)
- Next: Enums & enum class