In the previous lesson (10.2 -- Floating-point and integral promotion), we covered numeric promotions, which are conversions of specific narrower numeric types to wider numeric types (typically int or double) that can be processed efficiently.
C++ supports another category of numeric type conversions, called numeric conversions. These numeric conversions cover additional type conversions between fundamental types.
Key insight
Any type conversion covered by the numeric promotion rules (10.2 -- Floating-point and integral promotion) is called a numeric promotion, not a numeric conversion.
There are five basic types of numeric conversions.
- Converting an integral type to any other integral type (excluding integral promotions):
short s = 3; // convert int to short
long l = 3; // convert int to long
char ch = s; // convert short to char
unsigned int u = 3; // convert int to unsigned int
- Converting a floating point type to any other floating point type (excluding floating point promotions):
float f = 3.0; // convert double to float
long double ld = 3.0; // convert double to long double
- Converting a floating point type to any integral type:
int i = 3.5; // convert double to int
- Converting an integral type to any floating point type:
double d = 3; // convert int to double
- Converting an integral type or a floating point type to a bool:
bool b1 = 3; // convert int to bool
bool b2 = 3.0; // convert double to bool
As an aside…
Because brace initialization strictly disallows some types of numeric conversions (more on this next lesson), we use copy initialization in this lesson (which does not have any such limitations) in order to keep the examples simple.
Safe and unsafe conversions
Unlike numeric promotions (which are always value-preserving and thus “safe”), many numeric conversions are unsafe. An unsafe conversion is one where at least one value of the source type cannot be converted into an equal value of the destination type.
Numeric conversions fall into three general safety categories:
- Value-preserving conversions are safe numeric conversions where the destination type can exactly represent all possible values in the source type.
For example, int to long and short to double are safe conversions, as the source value can always be converted to an equal value of the destination type.
int main()
{
int n { 5 };
long l = n; // okay, produces long value 5
short s { 5 };
double d = s; // okay, produces double value 5.0
return 0;
}
Compilers will typically not issue warnings for implicit value-preserving conversions.
A value converted using a value-preserving conversion can always be converted back to the source type, resulting in a value that is equivalent to the original value:
#include <iostream>
int main()
{
int n = static_cast<int>(static_cast<long>(3)); // convert int 3 to long and back
std::cout << n << '\n'; // prints 3
char c = static_cast<char>(static_cast<double>('c')); // convert 'c' to double and back
std::cout << c << '\n'; // prints 'c'
return 0;
}
- Reinterpretive conversions are unsafe numeric conversions where the converted value may be different than the source value, but no data is lost. Signed/unsigned conversions fall into this category.
For example, when converting a signed int to an unsigned int:
int main()
{
int n1 { 5 };
unsigned int u1 { n1 }; // okay: will be converted to unsigned int 5 (value preserved)
int n2 { -5 };
unsigned int u2 { n2 }; // bad: will result in large integer outside range of signed int
return 0;
}
In the case of u1, the signed int value 5 is converted to unsigned int value 5. Thus, the value is preserved in this case.
In the case of u2, the signed int value -5 is converted to an unsigned int. Since an unsigned int can’t represent negative numbers, the result will be modulo wrapped to a large integral value that is outside the range of a signed int. The value is not preserved in this case.
Such value changes are typically undesirable, and will often cause the program to exhibit unexpected or implementation-defined behavior.
Related content
We discuss how out-of-range values are converted between signed and unsigned types in lesson 4.12 -- Introduction to type conversion and static_cast.
Warning
Even though reinterpretive conversions are unsafe, most compilers leave implicit signed/unsigned conversion warnings disabled by default.
