The Standard at § 8.5.4/7 explains what a narrowing conversion is:
A narrowing conversion is an implicit conversion
— from a floating-point type to an integer type, or
— from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or
— from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
— from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.
It then disallows such conversions in some list-initialization contexts, giving examples:
[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations. — end note ] [ Example:
int x = 999; // x is not a constant expression
const int y = 999;
const int z = 99;
char c1 = x; // OK, though it might narrow (in this case, it does narrow)
char c2{x}; // error: might narrow
char c3{y}; // error: narrows (assuming char is 8 bits)
char c4{z}; // OK: no narrowing needed
unsigned char uc1 = {5}; // OK: no narrowing needed
unsigned char uc2 = {-1}; // error: narrows
unsigned int ui1 = {-1}; // error: narrows
signed int si1 =
{ (unsigned int)-1 }; // error: narrows
int ii = {2.0}; // error: narrows
float f1 { x }; // error: might narrow
float f2 { 7 }; // OK: 7 can be exactly represented as a float
int f(int);
int a[] =
{ 2, f(2), f(2.0) }; // OK: the double-to-int conversion is not at the top level
— end example ]
All 7 of the errors illustrated by the examples are reported as such by
clang 3.2/3.3 with -std=c++11
, e.g.
error: non-constant-expression cannot be narrowed from type 'int' to 'char' in initializer list [-Wc++11-narrowing]
None of them are reported as errors by gcc 4.7.2/4.8.1, but in each case a similar warning is given, e.g.
warning: narrowing conversion of ‘x’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]
(so gcc seems to know what compliance requires but opts to tolerate non-compliance by default.)
What I do not understand is how the example:
unsigned int ui1 = {-1}; // error: narrows
qualifies as an example. (Likewise with the symmetrical si1
example.) Evidently
the only words by which it might qualify as example are those of the fourth
and final item in the definition of narrowing conversion given above; but
if so then why does the example not escape by the qualification
except where the source is a constant expression and the actual value after
conversion will fit into the target type and will produce the original value when converted back to the
original type? Surely -1
there is an integer constant and, if converted to unsigned
and back,
still yields int -1
?
What am I missing?
UINT_MAX
toint
typically yields-1
, but it's not guaranteed. – Jockstrap