在列表初始化中缩小到bool的转换 - 奇怪的行为 [英] Narrowing conversion to bool in list-initialization - strange behaviour

查看:906
本文介绍了在列表初始化中缩小到bool的转换 - 奇怪的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这段C ++ 11代码:

  #include< iostream> 

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

int main()
{
double d = 7.0;
X x {d};
}

在初始化<$时,从double转换为bool c $ c> x 。根据我对标准的理解,这是错误的代码,我们应该看到一些诊断。



Visual C ++ 2013发布错误:

 错误C2398:元素'1':从'double'转换为'bool'需要缩小转换

3.5.0和GCC 4.9.1,使用以下选项

  -Wall -Wextra -std = c ++ 11 -pedantic 

使用无错误和警告来编译此代码。运行程序会输出 1 (不出意外)。








更改 X(bool arg) X(int arg),突然,我们从Clang中得到一个错误

 错误:在初始化器列表中不能将类型'double'缩小为'int'[-Wc ++ 11-narrowing] 

和来自GCC的警告

 警告:缩小'd'从'double'到'int' } [-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,除非源是常量
表达式,实际值转换后的值在可以表示的值的范围内
(即使无法准确表示)或

- 从整数类型或无范围的枚举类型到浮点类型,除非源
是常量表达式,转换后的实际值将适合目标类型,并且当转换回原始类型时将
产生原始值,或

- 从整数类型或无范围的枚举类型到无法表示原始类型的所有
值的整数类型,除非源是常量表达式,而
转换后的实际值将适合目标类型,并且在转换回
原始类型时将产生原始值。

[注意:如上所述,在列表初始化中不允许在顶级进行此类转换。
note]


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


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


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

解决方案

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

  bool b {3}; 

两者 gcc clang 发出诊断,例如 gcc 说:


警告:将{3}从'int'转换为'bool'在{}中[-Wnarrowing]


包含在 C ++ 11标准草案 8.5.4 列表初始化段落:


缩小转换是隐式转换



[...]




  • 从整数类型或无范围的枚举类型转换为整数类型
    ,不能表示原始类型的所有值,
    除外,其中源是常量表达式和
    转换后的实际值将适合目标类型,并且在转换回原始类型时将生成原始
    值。


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

  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 整数类型的同义词是整数类型


您应该使用 clang a>和 gcc



Jonathan Wakely注意到EDG编译器给OPs代码提供了一个缩小的错误,这是一个强有力的指示,这确实应该产生一个诊断。



更新



我提交了 gcc clang 错误报告。



clang错误报告已更新为固定


在r229792中修复。



Consider this piece of C++11 code:

#include <iostream>

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

int main() 
{
    double d = 7.0;
    X x{d};
}

There's a narrowing conversion from a double to a bool in the initialization of x. According to my understanding of the standard, this is ill-formed code and we should see some diagnostic.

Visual C++ 2013 issues an error:

error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion

However, both Clang 3.5.0 and GCC 4.9.1, using the following options

-Wall -Wextra -std=c++11 -pedantic 

compile this code with no errors and no warnings. Running the program outputs a 1 (no surprise there).


Now, let's go deeper into strange territory.

Change X(bool arg) to X(int arg) and, suddenly, we've got an error from Clang

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

and a warning from GCC

warning: narrowing conversion of 'd' from 'double' to 'int' inside { } [-Wnarrowing]

This looks more like what I was expecting.


Now, keep the bool constructor argument (that is, revert to X(bool arg)), and change double d = 7.0; to int d = 7;. Again, a narrowing error from Clang, but GCC doesn't issue any diagnostic at all and compiles the code.

There are a few more behaviour variants that we can get if we pass the constant directly to the constructor, some strange, some expected, but I won't list them here - this question is getting too long as it is.


I'd say this is one of the rare cases when VC++ is right and Clang and GCC are wrong when it comes to standard-conformance, but, given the respective track records of these compilers, I'm still very hesitant about this.

What do the experts think?


Standard references (quotes from the final standard document for C++11, ISO/IEC 14882-2011):

In 8.5.4 [dcl.init.list] paragraph 3, we have:

— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.

In the same section, in paragraph 7, we have:

A narrowing conversion is an implicit conversion
— from a floating-point type to an integer type, or
— from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or
— from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
— from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.
[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations.—end note ]

In 3.9.1 [basic.fundamental] paragraph 7, we have:

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.48 A synonym for integral type is integer type.

(I was starting to question everything at this stage...)

解决方案

This simply looks like a bug, if we try the following:

bool b {3} ;

both gcc and clang issue a diagnostic, for example gcc says:

warning: narrowing conversion of '3' from 'int' to 'bool' inside { } [-Wnarrowing]

This is covered in the draft C++11 standard by section 8.5.4 List-initialization paragraph 7 which says:

A narrowing conversion is an implicit conversion

[...]

  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.

This is same paragraph that covers your example and the following simpler example:

bool a {3.0} ;

which would be covered by this bullet from paragraph 7 quoted above:

  • from a floating-point type to an integer type, or

From paragraph 3, this is ill-formed an requires a diagnostic:

List-initialization of an object or reference of type T is defined as follows:

[...]

  • Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

which gcc produces no diagnostic but clang does provide the following warning, although not the narrowing conversion warning we should see:

warning: implicit conversion from 'double' to 'bool' changes value from 3 to true [-Wliteral-conversion]

Note, section 3.9.1 [basic.fundamental] says:

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.48 A synonym for integral type is integer type.

You should file a bug report with both clang and gcc.

Jonathan Wakely notes that the EDG compiler gives a narrowing error for the OPs code, which is a strong indication that this indeed should produce a diagnostic.

Update

I submitted a gcc and clang bug report.

The clang bug report has been updated as fixed:

Fixed in r229792.

这篇关于在列表初始化中缩小到bool的转换 - 奇怪的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆