考虑这段 C++11 代码:
#include <iostream>
struct X
{
X(bool arg) { std::cout << arg << '\n'; }
};
int main()
{
double d = 7.0;
X x{d};
}
在x
. 根据我对标准的理解,这是格式错误的代码,我们应该看到一些诊断。
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
编译这段代码没有错误和警告。运行程序会输出 a 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 整数类型的同义词是整数类型。
(在这个阶段我开始质疑一切......)