【问题标题】:Narrowing conversion to bool in list-initialization - strange behaviour在列表初始化中缩小转换为 bool - 奇怪的行为
【发布时间】: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。
  • 已提交 clanggcc 错误报告。
  • clang 关闭错误报告已修复。
  • @ShafikYaghmour 好东西,感谢您的照顾。
  • 看起来 gcc 和 clang 都解决了这个问题。

标签: c++ c++11 standards-compliance narrowing list-initialization


【解决方案1】:

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

bool b {3} ;

gccclang 都发出诊断信息,例如 gcc 表示:

警告:在 { } [-Wnarrowing]

中缩小从 'int' 到 'bool' 的 '3' 转换

这在draft C++11 standard 部分8.5.4 List-initialization 段落7 中进行了介绍:

窄化转换是隐式转换

[...]

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

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

bool a {3.0} ;

上面引用的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 编译器为 OPs 代码提供了一个缩小错误,这强烈表明这确实应该产生诊断。

更新

我提交了gccclang 错误报告。

clang bug report has been updated as fixed:

在 r229792 中修复。

gcc bug report has been updated as fixed:

固定。

还有一个live example seems to confirm this

【讨论】:

  • 是的,使用 constant double 初始化 bool 的警告属于我在问题中提到的“更多行为变体”。基本上,在这种情况下,Clang 仍然没有检测到这是列表初始化中的缩小转换,而是回退到它为任何具有常量值的初始化发出的相同警告(如果你替换它会发出相同的警告带括号的大括号,使其成为允许的隐式转换)。
猜你喜欢
  • 2017-03-12
  • 2021-12-06
  • 1970-01-01
  • 2017-03-19
  • 1970-01-01
  • 2015-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多