VS2015 不一致的编译器行为

VS2015 inconsistent compiler behavior

我遇到了以下 VS2015 CTP6 VC++ 编译器行为:

unsigned char a = 1;

unsigned char x = (2 | a);   //this compiles fine

unsigned char y[] = {2 | a}; //this emits warning C4838: conversion from
                             //'int' to 'unsigned char' requires a narrowing conversion

我假设在 y 的定义中发生了一些隐式类型转换或提升,但无论那里发生了什么,也应该在定义 x 的行中发生 - 然而这一行编译正常。

颠倒操作数顺序没有帮助,将 2 转换为 unsigned char 也没有帮助:

unsigned char y[] = {(unsigned char)2 | a}; //same warning
unsigned char y[] = {a | 2};                //same warning

其他按位运算符也会发生同样的事情。解决警告的唯一方法是对按位运算的结果进行显式转换:unsigned char y[] = {(unsigned char)(2 | a)};

谁能解释这种编译器行为?这是一个错误吗?

编辑:

GCC 4.9.1 编译干净,Clang 3.5 发出错误:“错误:非常量表达式无法从类型 'int' 缩小到 'unsigned char' 在初始化列表中 [-Wc++11-narrowing]”。谁能解释一下?

(2 | a)int类型,因为2是int,a得到提升

C++ 从 C 继承了一种能力,可以静默执行从更宽类型到更小类型的不安全转换 - 在任何高阶位丢失的意义上是不安全的。

这就是它的作用:

unsigned char x = (2 | a);

新的大括号初始化语法是放弃这种向后兼容性的机会。按照设计,如果您在使用大括号初始化时想要 unsafe/lossy 转换,您必须提出要求。这就是为什么这不起作用:

unsigned char y[] = {2 | a}; 

int 初始化一个 unsigned char 会丢失信息,因此需要强制转换。

这不是错误,而是必需的行为。总有一天它会在编译时发现一个错误,如果它发生在 运行 时间,你可能需要花费数小时才能找到它,因此请尽可能使用新语法。

我不确定 Alan 的回答,因为初始化数组的大括号语法(这就是你在那里所做的)是有效的 C 语法,所以这对 C++ 来说并不新鲜。

在 VS2013 上,你的代码为我编译时没有任何警告。我最初说了一些其他的事情,但当我想到它时,我不确定就发出警告而言,什么是正确的。

从 C++03 到今天,聚合初始化规则发生了很大变化。 (来自第 [dcl.init.aggr] 部分的所有引述在所有版本中仍然是 8.5.1)

在 C++03 中,规则是

All implicit type conversions are considered when initializing the aggregate member with an initializer from an initializer-list.

在草案 n3485(大致为 C++11)和 n4296(大致为 C++14)中,这种情况现在由不同的规则涵盖

When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause. If the initializer-clause is an expression and a narrowing conversion is required to convert the expression, the program is ill-formed.

这是故意破坏向后兼容性,并在 C++11 附录中如此列出:

  • Change: Narrowing restrictions in aggregate initializers

  • Rationale: Catches bugs.

  • Effect on original feature: Valid C++ 2003 code may fail to compile in this International Standard. For example, the following code is valid in C++ 2003 but invalid in this International Standard because double to int is a narrowing conversion:

    int x[] = { 2.0 };
    

导致 OR-ing unsigned char 值产生不适合 unsigned char 而不缩小的结果的提升语义已在此处得到充分解释: