在列表初始化中缩小到布尔值的转换 - 奇怪的行为

Narrowing conversion to bool in list-initialization - strange behaviour

本文关键字:转换 布尔值 初始化 列表 缩小      更新时间:2023-10-16

考虑这段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 

编译此代码时没有错误和警告。运行程序会输出一个1(这并不奇怪(。


现在,让我们更深入地进入奇怪的领域。

X(bool arg)更改为X(int arg),突然,我们收到了来自 Clang 的错误

error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]

以及海湾合作委员会的警告

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段中,我们有:

缩小转换是隐式转换
— 从浮点类型到整数类型,或
— 从长双精度到双精度或浮点数,或从双精度到浮点数,除非源是常数 表达式和转换后的实际值在可以表示的值范围内 (即使不能准确表示(,或
— 从整数类型或无作用域枚举类型到浮点类型,源除外 是一个常量表达式,转换后的实际值将适合目标类型,并且 转换回原始类型时生成原始值,或
— 从整数类型或无作用域枚举类型到不能表示所有 原始类型的值,除非源是常量表达式且实际值在之后 转换将适合目标类型,并在转换回 原始类型。
[ 注意:如上所述,在列表初始化的顶层不允许进行此类转换。 注]

在 3.9.1 [基本.基本] 第 7 段中,我们有:

布尔、字符、char16_t、char32_t、wchar_t 类型以及有符号和无符号整数类型是集合的 称为整型.48 整型的同义词是整型。

(我在这个阶段开始质疑一切...

如果我们尝试以下操作,这看起来只是一个错误:

bool b {3} ;

gccclang都会发出诊断,例如gcc说:

警告:在 { } 内将"3"从"int"缩小到"bool"的转换范围 [-缩小]

这在C++11标准草案第8.5.4节的清单初始化第7段中有所涉及,该段规定:

缩小转换是隐式转换

[...]

    从整数类型
  • 或无作用域枚举类型到整数类型 不能表示原始类型的所有值,除了 其中源是常量表达式,之后的实际值 转换将适合目标类型,并将生成原始 转换回原始类型时的值。

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

bool a {3.0} ;

上文引述的第7段中的此项目符号将涵盖:

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

从第 3 段来看,这是格式不正确的,需要诊断:

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

[...]

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

gcc不会产生诊断信息,但clang确实提供了以下警告,尽管不是我们应该看到的缩小转换范围警告:

警告:从"双精度"到"布尔"的隐式转换会将值从 3 更改为 true [-文字转换]

注意,[basic.basic]3.9.1 节说:

布尔、字符、char16_t、char32_t、wchar_t 类型以及有符号和无符号整数类型是集合的 称为整型.48 整型的同义词是整型

您应该同时向clang和gcc提交错误报告。

Jonathan Wakely 指出,EDG 编译器为 OPs 代码提供了缩小错误,这强烈表明这确实应该产生诊断。

更新

我提交了一份 gcc 和 clang 错误报告。

clang 错误报告已更新为已修复:

在 r229792 中修复。

gcc 错误报告已更新为已修复:

固定。

一个活生生的例子似乎证实了这一点。