Skip to content
Theory · Casts and Aliases · lesson 1 of 6

Implicit conversions and static_cast

The sum of 3 and -5 is -2, but this program prints a very different number:

C++
#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, char and short are promoted to int: true + true is an int with the value 2.
  • If the types still differ, both operands are converted to a common type. int and double give double, int and long long give long long, int and unsigned give unsigned.

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.

C++
#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:

C++
#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:

C++
#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.