为何列表初始化的缩窄转换未被检测?VS2017编译异常问询
咱们先明确C++标准§8.5.4的规定:使用列表初始化(比如int x{ 5 };这种形式)时,如果需要对初始化参数执行缩窄转换,程序属于病态(ill-formed)。这里的缩窄转换指的是:从整数类型或无作用域枚举类型转换为无法表示原类型所有值的整数类型,但如果源是常量表达式且经整型提升后的值能适配目标类型的情况除外。
不过在Visual Studio Community 2017 Version 15.9.5环境下(开启/wall并将警告视为错误,x64 Debug模式,C++11/14/17标准),我遇到了不符合标准的编译行为,下面分三种情况说明:
可正常编译的异常情况:
char c{ 'A' }; char x{ c + c };要知道
c + c的结果是int类型(可以通过static_assert(std::is_same_v<decltype(c + c), int>, "");验证),这个从int到char的转换属于非常量表达式的缩窄转换,按照标准编译器本应报错,但实际却编译通过了。符合预期的编译失败(目标为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: 从'int'转换为'char'需要缩窄转换,这也符合标准的规定。
这种不一致的行为明显是Visual Studio 2017编译器的bug——它在直接用c + c初始化char变量时,没有正确检测到这是一个非常量表达式的缩窄转换;而当目标类型改为unsigned char,或者把计算结果存入临时int变量后,又能正确触发错误提示。我已经向微软提交了这个bug报告。
内容的提问来源于stack exchange,提问作者Stack Danny

