为什么缩小使用花括号分隔初始值设定项的转换会导致错误?

时间:2015-07-28 19:09:37

标签: c++ c++11 narrowing

我在The C ++ Programming Language,4th ed中学习了花括号分隔的初始化器。 >第2章:C ++之旅:基础知识。

我引用了下面这本书。

  

= form是传统的,可以追溯到C,但如果有疑问,请使用通用{} -list表单(第6.3.5.2节)。   如果不出意外,它可以帮助您避免丢失信息的转换(缩小转换次数;§10.5):

int i1 = 7.2;    // i1 becomes 7
int i2 {7.2};    // error : floating-point to integer conversion
int i3 = {7.2};  // error : floating-point to integer conversion (the = is redundant)

但是,我无法重现这些结果。

我有以下代码。

#include <iostream>

int main()
{
    int i1 = 7.2;
    int i2 {7.2};
    int i3 = {7.2};

    std::cout << i1 << "\n";
    std::cout << i2 << "\n";
    std::cout << i3 << "\n";
}

当我编译并运行它时,我没有收到任何错误。我收到关于std=c++11但没有错误的警告。

$ g++ init.cpp 
init.cpp: In function ‘int main()’:
init.cpp:6:12: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
     int i2 {7.2};
            ^
$ ./a.out 
7
7
7

此外,警告仅适用于第二次分配,但第三次分配没有警告。这似乎表明=并不像书中提到的那样多余。如果=是多余的,则第二次和第三次分配都会产生警告,或者两者都不会产生警告。 然后我用-std=c++11标志编译它们。

$ g++ -std=c++11 init.cpp 
init.cpp: In function ‘int main()’:
init.cpp:6:16: warning: narrowing conversion of ‘7.2000000000000002e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
     int i2 {7.2};
                ^
init.cpp:7:18: warning: narrowing conversion of ‘7.2000000000000002e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
     int i3 = {7.2};
                  ^
$ ./a.out 
7
7
7

仍然没有错误。只有警告。虽然在这种情况下,第二次和第三次分配在产生警告方面的行为相同。

所以我的问题是:尽管本书提到第二和第三个作业都是错误,为什么这个代码不能编译?

2 个答案:

答案 0 :(得分:13)

这是不正确的,应该有诊断,但它可以是警告(你收到)或错误。 gcc made this a warning for several versions due to porting issue from C++03

  

该标准仅要求符合条件的实施方案应至少发出一条诊断信息&#34;所以允许用警告编译程序。正如Andrew所说,-Werror = narrowing允许你在你想要的时候把它变成错误。

     

G ++ 4.6给出了一个错误,但是因为很多人(包括我自己)发现在尝试编译大型C ++ 03代码库时最常遇到的问题之一变为C + +11。以前格式良好的代码,例如char c [] = {i,0}; (其中我只会在char范围内)导致错误并且必须更改为char c [] = {(char)i,0}

但是现在gcc和clang的最新版本使这个错误see it live for gcc

作为参考,draft C++11 standard部分8.5.4 [dcl.init.list] 表示:

  

否则,如果初始化列表具有单个元素,则对象或   引用从该元素初始化;如果缩小转换   (见下文)需要将元素转换为T,程序是   病态的。 [例如:

int x1 {2}; // OK
int x2 {2.0}; // error: narrowing
     

-end example]

  

缩小转化是隐式转化

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

[...]

     

[注意:如上所述,list-initializations.-end中的顶层不允许进行此类转换   注意] [例子:

     

[...]

int ii = {2.0}; // error: narrows
     

[...]

因此,浮点到整数转换是一种缩小的转换,并且格式不正确。

和部分1.4实施合规性[intro.compliance]说:

  

虽然本国际标准仅规定了对C ++实现的要求,但这些要求   如果它们被表达为对程序,程序部分或者程序的要求,则通常更容易理解   执行程序。这些要求具有以下含义:

     

[...]

     
      
  • 如果某个程序包含违反任何可诊断规则或发生的描述   当实现不支持该构造时,此标准为“有条件支持”,   符合要求的实施应至少发布一条诊断信息。
  •   
     

[...]

告诉我们只需要诊断。

答案 1 :(得分:8)

C ++语言不区分“警告”和“错误”。 C ++只有诊断消息。您收到的警告是诊断消息。语言规范不要求编译器在遇到错误(也就是格式错误的)代码时停止编译。所有编译器必须做的是发出诊断消息,然后如果他们愿意,他们可以继续编译。

这意味着在一般情况下,您有责任告知警告“警告”,这些警告实际上表示真正的错误,特别是对于像GCC这样的许可编译器。

这也意味着实际的真实行为是编译器设置的问题。如果可能的话,请求您的编译器在这方面更具限制性。在GCC中,您可以尝试-pedantic-errors为此目的切换。

P.S。在我使用GCC的实验中,-std=c++11足以让它为您的代码生成错误。如果您收到警告,则可能是编译器版本问题。