Why doesn't narrowing conversion used with curly-brace-delimited initializer cause an error?

This is ill-formed and there should be diagnostic, however it can either be a warning(which you received) or an error. gcc made this a warning for several versions due to porting issue from C++03:

The standard only requires that "a conforming implementation shall issue at least one diagnostic message" so compiling the program with a warning is allowed. As Andrew said, -Werror=narrowing allows you to make it an error if you want.

G++ 4.6 gave an error but it was changed to a warning intentionally for 4.7 because many people (myself included) found that narrowing conversions where one of the most commonly encountered problems when trying to compile large C++03 codebases as C++11. Previously well-formed code such as char c[] = { i, 0 }; (where i will only ever be within the range of char) caused errors and had to be changed to char c[] = { (char)i, 0 }

but now recent versions of gcc and clang make this an error, see it live for gcc.

For reference the draft C++11 standard section 8.5.4 [dcl.init.list] says:

Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed. [ Example:

int x1 {2}; // OK
int x2 {2.0}; // error: narrowing

—end example ]

and:

A narrowing conversion is an implicit conversion

  • from a floating-point type to an integer type, or

[...]

[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations.—end note ] [ Example:

[...]

int ii = {2.0}; // error: narrows

[...]

So a floating point to integer conversion is a narrowing conversion and is ill-formed.

and section 1.4 Implementation compliance [intro.compliance] says:

Although this International Standard states only requirements on C++ implementations, those requirements are often easier to understand if they are phrased as requirements on programs, parts of programs, or execution of programs. Such requirements have the following meaning:

[...]

  • If a program contains a violation of any diagnosable rule or an occurrence of a construct described in this Standard as “conditionally-supported” when the implementation does not support that construct, a conforming implementation shall issue at least one diagnostic message.

[...]

Tells us that only a diagnostic is required.


C++ language does not distinguish "warnings" from "errors". C++ only has diagnostic messages. The warnings you received are diagnostic messages. The language specification does not require compilers to stop compilation when they encounter erroneous (aka ill-formed) code. All compilers have to do is issue a diagnostic message, and then they can continue compiling, if they so desire.

This means that in general case it is your responsibility to tell warnings that are "just warnings" from warnings that actually indicate genuine errors, especially with such permissive compilers as GCC.

This also means that the actual real-life behavior is a matter of your compiler setup. Ask your compiler to be more restrictive in that regard, if possible. In GCC you might try -pedantic-errors switch for that purpose.

P.S. In my experiments with GCC, -std=c++11 is sufficient to make it generate errors for your code. If you are getting warnings instead, it could be a matter of compiler version.