【发布时间】:2015-02-14 22:12:29
【问题描述】:
考虑这段 C++11 代码:
#include <iostream>
struct X
{
X(bool arg) { std::cout << arg << '\n'; }
};
int main()
{
double d = 7.0;
X x{d};
}
在x 的初始化过程中,有一个从 double 到 bool 的缩小转换。根据我对标准的理解,这是格式错误的代码,我们应该看到一些诊断。
Visual C++ 2013 报错:
error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion
但是,Clang 3.5.0 和 GCC 4.9.1 都使用以下选项
-Wall -Wextra -std=c++11 -pedantic
编译此代码时没有错误和警告。运行程序会输出1(这并不奇怪)。
现在,让我们深入到陌生的领域。
将X(bool arg) 更改为X(int arg),突然,我们收到了来自 Clang 的错误
error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
来自 GCC 的警告
warning: narrowing conversion of 'd' from 'double' to 'int' inside { } [-Wnarrowing]
这看起来更像我的预期。
现在,保留bool 构造函数参数(即恢复为X(bool arg)),并将double d = 7.0; 更改为int d = 7;。再次,来自 Clang 的缩小错误,但 GCC 根本不发出任何诊断信息并编译代码。
如果我们将常量直接传递给构造函数,我们可以获得更多的行为变体,有些奇怪,有些是预期的,但我不会在这里列出它们 - 这个问题已经变得太长了。
我想说这是在标准一致性方面 VC++ 正确而 Clang 和 GCC 错误的罕见情况之一,但是鉴于这些编译器各自的跟踪记录,我仍然非常犹豫这。
专家们怎么看?
标准参考(引自 C++11 最终标准文档,ISO/IEC 14882-2011):
在 8.5.4 [dcl.init.list] 第 3 段中,我们有:
——否则,如果 T 是类类型,则考虑构造函数。枚举适用的构造函数 并且通过重载决议(13.3、13.3.1.7)选择最好的一个。如果缩小转换(见 下面)需要转换任何参数,程序格式错误。
在同一部分的第 7 段中,我们有:
缩小转换是一种隐式转换
— 从浮点类型到整数类型,或
— 从 long double 到 double 或 float,或从 double 到 float,除非源是常量 表达式和转换后的实际值在可以表示的数值范围内 (即使不能准确表示),或者
— 从整数类型或无作用域枚举类型到浮点类型,除非源 是一个常量表达式,转换后的实际值将适合目标类型并且将 转换回原始类型时产生原始值,或
— 从整数类型或无作用域枚举类型到不能代表所有的整数类型 原始类型的值,除非源是常量表达式并且后面的实际值 转换将适合目标类型,并在转换回 原始类型。
[注意:如上所述,列表初始化中的顶层不允许进行此类转换。-结束 注意]
在 3.9.1 [basic.fundamental] 第 7 段中,我们有:
类型bool、char、char16_t、char32_t、wchar_t,有符号和无符号整数类型统称为 称为整数类型。48 整数类型的同义词是整数类型。
(在这个阶段我开始质疑一切......)
【问题讨论】:
-
嘿,所有的cmets都去哪儿了?其中一些包含用于诊断问题的有用信息,尤其是对于 Clang。
-
clang 关闭错误报告已修复。
-
@ShafikYaghmour 好东西,感谢您的照顾。
-
看起来 gcc 和 clang 都解决了这个问题。
标签: c++ c++11 standards-compliance narrowing list-initialization