Implicit conversions and static_cast
The sum of 3 and -5 is -2, but this program prints a very different number:
#include <iostream>
int main() {
int change = -5;
unsigned stock = 3u;
std::cout << stock + change << '\n'; // 4294967294
}Before the addition, the compiler converted both operands to one type, and that type turned out to be unsigned. It has no negative numbers, so the sum came out non-negative as well.
Implicit conversions
An implicit conversion is a type change that the compiler makes on its own, without being asked in the code. Before arithmetic and comparisons, it brings the operands to a common type by two rules:
bool,charandshortare promoted toint:true + trueis anintwith the value2.- If the types still differ, both operands are converted to a common type.
intanddoublegivedouble,intandlong longgivelong long,intandunsignedgiveunsigned.
Together these rules are called the usual arithmetic conversions. A negative int value converted to unsigned wraps around, and the standard defines the result. -1 becomes the largest unsigned value, and -5 becomes the number four below it. Comparisons work the same way: < with an int operand and an unsigned one compares the converted values.
The other direction is riskier. A double value stored in an int loses its fraction, and a long long may not fit in an int. These are narrowing conversions: with = they compile without an error, while braces, as the topic on variables showed, refuse them.
Casting with static_cast
When the compiler will not make a needed conversion on its own, the type is changed explicitly. Such an expression is called a cast. static_cast<double>(sum) gives the value of sum as a double, while the variable itself stays an int.
#include <iostream>
int main() {
int sum = 7;
int count = 2;
std::cout << static_cast<double>(sum) / count << '\n'; // 3.5
}The dividend is now a double, so count is converted to double too, and the division keeps the fraction.
The same technique guards against overflow. The smallest and largest int values are held by the constants INT_MIN and INT_MAX from the <climits> header:
#include <climits>
#include <iostream>
int main() {
int a = INT_MAX;
int b = 1;
long long sum = static_cast<long long>(a) + b;
std::cout << sum << '\n'; // 2147483648
}In int, the sum a + b would overflow, and that is undefined behavior. Once a is cast to long long, b goes to long long as well, and the addition happens in the wider type.
In C, a type is changed with the notation (double)sum. It works in C++ too, but it can also remove const or reinterpret a pointer. The code does not show which of these was meant. A named cast such as static_cast does one job, and a search for its name finds every place in the code.
Standard library types define conversions of their own: a string literal implicitly becomes a std::string, and a std::string becomes a std::string_view. The author of a type writes such rules, and the compiler applies them implicitly as well.
Common mistake
The cast is applied to the result of the division instead of an operand:
#include <iostream>
int main() {
int sum = 7;
int count = 2;
double avg = static_cast<double>(sum / count); // ✗
std::cout << avg << '\n'; // 3
}The program prints 3. The parentheses hold the integer division 7 / 2, and only its result 3 becomes a double. Cast an operand instead: static_cast<double>(sum) / count gives 3.5.