15

我在 The C++ Programming Language,第 4 版中了解了花括号分隔的初始化程序。> 第 2 章:C++ 之旅:基础知识。

我从下面的书中引用。

= 形式是传统的,可以追溯到 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

仍然没有错误。只有警告。尽管在这种情况下,第二个和第三个分配在生成警告方面表现相同。

所以我的问题是:虽然书中提到第二个和第三个赋值是错误的,为什么这段代码没有编译失败?

4

2 回答 2

15

这是格式错误的,应该有诊断,但是它可以是警告(您收到)或错误。由于 C++03 的移植问题,gcc 对多个版本发出了警告

该标准仅要求“符合要求的实现应发出至少一个诊断消息”,因此允许编译带有警告的程序。正如安德鲁所说, -Werror=narrowing 允许您根据需要将其设为错误。

G++ 4.6 给出了一个错误,但它被故意更改为 4.7 的警告,因为许多人(包括我自己)发现缩小转换是尝试将大型 C++03 代码库编译为 C++11 时最常见的问题之一。以前格式正确的代码,例如 char c[] = { i, 0 }; (我只会在 char 范围内)导致错误,必须更改为 char c[] = { (char)i, 0 }

但是现在最新版本的 gcc 和 clang 使这成为一个错误,看到它为 gcc 直播

作为参考草案 C++11 标准部分8.5.4 [dcl.init.list]说:

否则,如果初始化列表只有一个元素,则从该元素初始化对象或引用;如果需要缩小转换(见下文)将元素转换为 T,则程序格式错误。[ 例子:

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

—结束示例]

和:

缩小转换是隐式转换

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

[...]

[注意:如上所述,列表初始化中的顶级不允许此类转换。-结束注释] [示例:

[...]

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

[...]

因此,浮点到整数的转换是一种窄化转换并且格式错误。

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

尽管该国际标准仅规定了对 C++ 实现的要求,但如果将这些要求表述为对程序、程序部分或程序执行的要求,则通常更容易理解。此类要求具有以下含义:

[...]

  • 如果程序包含违反任何可诊断规则或出现在本标准中描述为“有条件支持”的构造,而实现不支持该构造,则符合要求的实现应发出至少一个诊断消息。

[...]

告诉我们只需要诊断。

于 2015-07-28T19:29:47.580 回答
10

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

这意味着在一般情况下,您有责任将“只是警告”的警告与实际指示真正错误的警告区分开来,尤其是对于像 GCC 这样的许可编译器。

这也意味着实际的实际行为取决于您的编译器设置。如果可能,请让您的编译器在这方面更具限制性。在 GCC 中,您可能会-pedantic-errors为此尝试 switch。

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

于 2015-07-28T19:11:53.170 回答