为什么这个缩小转换没有被检测到?

16

在使用列表初始化构造变量时(比如 int x{ 5 };),标准 §8.5.4 规定:

如果需要进行缩小转换[...]以转换任何一个参数,则程序是非法的。 (7) 缩小转换是一种隐式转换 - (7.4) 从整数类型或未经作用域限定的枚举类型到整数类型的转换,该整数类型不能表示原始类型的所有值,但源自常量表达式的情况除外,并且在积分提升后其值将适合目标类型。

那么为什么这段代码可以编译通过?

char c{ 'A' };
char x{ c + c };
作为提醒,c + c的结果是一个int
static_assert(std::is_same_v<decltype(c + c), int>, "");

编译器应该会报告一个缩小转换的错误,这明显不是一个常量表达式。


有趣的是,将 x 声明为 unsigned char 会正确地导致编译失败:

char c{ 'A' };
unsigned char x{ c + c }; 

C2397将'int'转换为'unsigned char'需要进行缩小转换

引入临时变量也是如此:

char c{ 'A' };
int sum{ c + c };
char x{ sum }; //C2397 conversion from 'int' to 'char' requires [...]
那么为什么第一个版本会编译通过呢?我使用的是Visual Studio Community 2017版本15.9.5,使用/wall编译,并启用所有警告均视为错误,在x64调试构建中编译。设置标准为C++11、C++14和C++17时都可以编译通过。 我在这里提交了错误报告

3
使用clang编译这段代码会报错,而使用gcc则会发出警告。 - interjay
你使用了哪些编译标志?是否使用了“-Wall”或“-Wnarrowing”?(或者它们在Visual Studio中的等效标志。) - Tarick Welling
看起来Biagio Festa的答案是正确的,与Visual Studio Bug有关。 - Tarick Welling
1个回答

12

是的。你说得对:程序存在问题。

在这种情况下(标准 §1.4):

符合规范的实现应该发出至少一条诊断消息。

事实上,gcc 会产生一个警告信息。 clang 直接将代码拒绝为编译器错误。

这个特定的主题已经在这里讨论过here,针对gcc1

Visual Studio 应该生成诊断消息(建议您检查编译选项。您是否禁用了警告?是否使用 C++(11/14/17) 编译?...)。如果不是这种情况,则是实现错误。

更新:

Visual Studio v19.20 不会产生任何诊断消息(即使使用 /Wall 标志)。

A bug report 已在此处填写。


1 关于gcc实现缩窄检查的更多信息,请在此处查看。


你使用的是哪个C++标准? 如果你提交了一个错误报告,我认为将链接作为评论添加到这个答案中会很有用。谢谢。 - BiagioF
我实际上切换了C++11-C++14-C++17来看看是否有区别,但是没有发现任何区别,所以我只标记了C++11。 - Stack Danny
是的,你只需要C++11或以上版本。 - BiagioF
1
是的,我写了一个简短的摘要并链接了这篇文章。如果有帮助的话。 - Stack Danny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接