为什么"unsigned int ui = {-1};" 缩小转换错误?

Mik*_*han 4 c++ type-conversion language-lawyer narrowing c++11

第8.5.4/7节中的标准解释了缩小转换的含义:

缩小转换是隐式转换

- 从浮点类型到整数类型,或

- 从long double到double或float,或从double到float,除非source是常量表达式,转换后的实际值在可以表示的值范围内(即使它不能精确表示),或者

- 从整数类型或无范围枚举类型到浮点类型,除非源是常量表达式,转换后的实际值将适合目标类型,并在转换回原始类型时生成原始值,要么

- 从整数类型或未范围的枚举类型到不能表示原始类型的所有值的整数类型,除非源是常量表达式,并且转换后的实际值将适合目标类型并将生成原始值转换回原始类型时.

然后它在一些列表初始化上下文中不允许这样的转换,给出了示例:

[注意:如上所述,列表初始化中的顶层不允许进行此类转换. - 尾注] [示例:

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
Run Code Online (Sandbox Code Playgroud)

- 结束例子]

示例中说明的所有7个错误都由clang 3.2/3.3报告-std=c++11,例如

error: non-constant-expression cannot be narrowed from type 'int' to 'char' in initializer list [-Wc++11-narrowing]
Run Code Online (Sandbox Code Playgroud)

它们都没有被gcc 4.7.2/4.8.1报告为错误,但在每种情况下都给出了类似的警告,例如

warning: narrowing conversion of ‘x’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]
Run Code Online (Sandbox Code Playgroud)

(所以gcc似乎知道合规性需要什么,但默认情况下选择容忍违规.)

我不明白的是这个例子:

unsigned int ui1 = {-1};    // error: narrows
Run Code Online (Sandbox Code Playgroud)

有资格作为一个例子.(与对称的si1例子相同.)显然,它可以作为例子的唯一单词是上面给出的缩小转换定义中的第四个也是最后一个项目的单词; 但是如果是这样的话,那么为什么示例不会被限定条件转义, 除非源是一个常量表达式,转换后的实际值将适合目标类型并在转换回原始类型时产生原始值?当然-1有一个整数常量,如果转换为unsigned和返回,仍然会产生int -1

我错过了什么?

R. *_*des 6

当然-1有一个整数常量,如果转换为unsigned并返回,仍然产生int -1?

这是错的.如果您将-1转换为unsigned您获得UINT_MAX.这很好,因为始终定义转换为无符号类型.但是,UINT_MAX如果int值适合目标类型,则仅适用于签名类型的转换并且仅由标准定义.