Skip to content

19 Beginner The Hundred

The Initializer That Says No

two values shrink, and nothing stops them

#include <iostream>

int main() {
    long total = 300;

    int count = 3.9;     // 3.9 of them, near enough
    char code = total;   // 300 is a small number

    std::cout << "count = " << count << "\n";
    std::cout << "code  = " << static_cast<int>(code) << "\n";
}

Run it. What does it print — and what does the compiler have to say about it?

Answer
count = 3
code  = 44

Not a peep from GCC, even with -Wall -Wextra. Swap = for {} and both lines draw a diagnostic — the first one fatal.

Why

= initialization runs the old C conversion rules, which drop whatever does not fit: doubleint chops the fraction (3.9 becomes 3 — it never rounds) and longchar keeps the low byte (300 becomes 44, the code for ','). Braced initialization, new in C++11, refuses narrowing conversions — anything the type system cannot call value-preserving — so the same two lines now draw the diagnostic the standard demands; try -DSHOW_BUG:

error: narrowing conversion of '3.8999999999999999e+0' from 'double' to 'int' [-Wnarrowing]
warning: narrowing conversion of 'total' from 'long int' to 'char' [-Wnarrowing]

Mind the two severities: GCC 11.5 errors on the constant 3.9 but only warns on the run-time total — a warning is still a conforming diagnostic, so add -Werror=narrowing for a hard rejection (Clang 18.1 rejects both on its own). The rule judges types, not luck: int n{total} is narrowing even though 300 fits in an int, while int n{300} is fine — only constant integers get that pass, and even int n{3.0} is narrowing. -Wconversion (in neither -Wall nor -Wextra) catches the = spellings too.

The fix

Initialize with braces — and when you really do want the loss, say so out loud, which also forces you to decide which loss you meant (std::lround needs <cmath>):

int count{static_cast<int>(3.9)};                  // 3 — truncation, on purpose
int rounded{static_cast<int>(std::lround(3.9))};   // 4 — rounding, on purpose

Takeaway: {} rejects the lossy conversions that = waves through — one more reason to make braces your default.

Try it: g++ -std=c++17 main.cpp -o demo && ./demo — then again with -DSHOW_BUG

Open in Compiler Explorer ↗ Open SHOW_BUG variant ↗ Quiz this entry