在列表初始化中缩小转换为bool - 奇怪的行为

bog*_*dan 24 c++ standards-compliance narrowing c++11 list-initialization

考虑一下这段C++ 11代码:

#include <iostream>

struct X
{
    X(bool arg) { std::cout << arg << '\n'; }
};

int main() 
{
    double d = 7.0;
    X x{d};
}
Run Code Online (Sandbox Code Playgroud)

在初始化过程中,从double到bool的转换范围正在缩小x.根据我对标准的理解,这是错误的代码,我们应该看到一些诊断.

Visual C++ 2013发出错误:

error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion
Run Code Online (Sandbox Code Playgroud)

但是,Clang 3.5.0和GCC 4.9.1都使用以下选项

-Wall -Wextra -std=c++11 -pedantic 
Run Code Online (Sandbox Code Playgroud)

编译此代码没有错误,也没有警告.运行程序输出1(不出意外).


现在,让我们深入到陌生的领域.

改变X(bool arg)X(int arg)和,突然间,我们从锵得到一个错误

error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
Run Code Online (Sandbox Code Playgroud)

海湾合作委员会发出警告

warning: narrowing conversion of 'd' from 'double' to 'int' inside { } [-Wnarrowing]
Run Code Online (Sandbox Code Playgroud)

这看起来更像我期待的.


现在,保持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,除非source是常量表达式,转换后的实际值是在可以表示的值范围内(即使它不能精确表示),或者
- 从整数类型或无范围枚举类型到浮点类型,除非源是常量表达式和转换后的实际值将适合目标类型并在转换回原始类型时生成原始值,或者
- 从整数类型或无范围枚举类型转换为不能表示原始类型的所有值的整数类型,除非源是常量表达式和转换后的实际值将适合目标类型,并在转换回原始类型时生成原始值.
[注意:如上所述,列表初始化中顶层不允许进行此类转换.-end note]

在3.9.1 [basic.fundamental]第7段中,我们有:

类型bool,char,char16_t,char32_t,wchar_t以及有符号和无符号整数类型统称为整数类型.整数类型的同义词是整数类型.

(我现阶段开始质疑一切......)

Sha*_*our 16

如果我们尝试以下操作,这看起来就像一个bug:

bool b {3} ;
Run Code Online (Sandbox Code Playgroud)

两者gccclang发布诊断,例如gcc说:

警告:缩小{3'从'int'到'bool'的转换{} [-Wnarrowing]

这将在C++ 11标准草案中通过8.5.4 列表初始化7段进行介绍,其中说明:

缩小转换是隐式转换

[...]

  • 从整数类型或未范围的枚举类型到不能表示原始类型的所有值的整数类型,除非source是常量表达式,并且转换后的实际值将适合目标类型并且将在生成原始值时生成原始值转换回原始类型.

这是涵盖您的示例和以下更简单示例的相同段落:

bool a {3.0} ;
Run Code Online (Sandbox Code Playgroud)

这篇文章将从7上面引用的段落中涵盖:

  • 从浮点类型到整数类型,或

从段落来看3,这是一个不完整的需要诊断:

列表初始化对象或类型T的引用定义如下:

[...]

  • 否则,如果初始化列表具有单个元素,则从该元素初始化对象或引用; 如果需要缩小转换(见下文)将元素转换为T,则程序格式不正确.

gcc不会产生任何诊断,但clang确实提供了以下警告,虽然不是收缩转换警告我们应该看到:

警告:从'double'到'bool'的隐式转换将值从3更改为true [-Wliteral-conversion]

注意,3.9.1 [basic.fundamental]部分说:

类型bool,char,char16_t,char32_t,wchar_t以及有符号和无符号整数类型统称为整数类型 .48 整数类型的同义词是整数类型.

您应该使用clanggcc提交错误报告.

Jonathan Wakely指出,EDG编译器为OP代码提供了一个缩小的误差,这表明这确实应该产生诊断.

更新

我提交了一份gccclang bug报告.

为固定铛错误报告已更新:

已在r229792中修复.

为固定gcc的bug报告已更新:

固定.

一个现实的例子似乎证实了这一点.