Type Conversions and Casting in C++
A type conversion happens when a value of one type is treated as, or turned into, a value of another type.
There are two flavors: conversions the compiler does for you automatically, called implicit conversions, and conversions you trigger manually, called explicit conversions or casts.
Safe implicit conversions
C++ will happily convert a value from a smaller type to a larger one without you asking. This is called widening, and it’s safe because no information is lost. You’re just fitting a smaller thing into a bigger box.
Even though this prints 95 with no decimal point, average does hold a double and not an int. By default, std::cout just doesn’t show trailing zeros.
The same thing happens going from char to a wider type like double. Every character has an underlying number that the machine actually stores, called its ASCII value. It is this ASCII value that gets widened and stored in the double:
The ASCII value of ‘A’ is 65, which is what the output shows.
The following diagram maps out the full widening path. The compiler handles this conversion silently. Going the other direction is where things get interesting.
Risky implicit conversions
If you try to fit a larger type into a smaller one, you might lose information. This is called a narrowing conversion.
C++ will still do it implicitly in many contexts, but it will silently truncate or discard data:
Notice that 9.99 didn’t round to 10. It truncated with the decimal portion simply cut off.
The same thing happens going the other way on the integer side:
As char holds -128 to 127 on most systems, you end up with some garbage value in tiny after the implicit conversion from int to char. The compiler may not even warn you about this.
Explicit casts
A cast explicitly says “I know what I’m doing, just convert this blindly.” In modern C++, the standard way to cast is by using static_cast<TargetType>(value):
This does the same thing as the implicit conversion above, but now it’s intentional and visible. Someone reading your code sees static_cast<int> and knows you made a conscious choice to truncate, rather than wondering if it was an accident.
Going the other direction is just as useful:
Without the cast, int code = grade; would do the same thing implicitly. The cast just makes the intent explicit.
What static_cast does and doesn’t do
static_cast is a compile-time check. The compiler verifies that the conversion is at least plausible. You can’t static_cast a double into a string literal, for instance, because those types are totally unrelated. It stops you from doing things that make no sense, while still letting you do things that do make sense, even if they’re a bit risky.
The old-style C cast, and why to avoid it
You might see code like this in older examples:
double d = 9.99;int i = (int)d; // C-style castThis also works, but it’s weaker in the sense that it’ll silently allow some genuinely dangerous conversions that static_cast would refuse. They’re also harder to search for in a codebase. We should prefer static_cast in modern code.
A summary of conversions
| From | To | What happens |
|---|---|---|
int | double | Safe, automatic |
char | int | Safe, automatic (gives the character’s ASCII value) |
double | int | May lose information |
int | char | May lose information |
Before converting, ask yourself whether information could be lost. If it can, either make sure that’s intentional and add a static_cast, or restructure the code so the conversion happens in the safe direction.