3

考虑这段代码:

#include <iostream>

int main()
{
    int i{10.1}; // narrowing, should not compile
    std::cout << i << std::endl;
}

根据 C++11 标准,它不应该编译(大括号初始化中的变窄是禁止的。)

现在,编译g++4.9.2 -std=c++11只发出警告

warning: narrowing conversion of '1.01e+1' from 'double' to 'int' inside { } [-Wnarrowing]

删除-std=c++11标志会导致有关大括号初始化的警告,但不会缩小:

warning: extended initializer lists only available with -std=c++11 or -std=gnu++11

另一方面,g++5不会编译它,前提是您使用g++5 -std=c++11. 但是,如果-std=c++11省略了,那么甚至可以g++5愉快地编译它,只给出与大括号 init 相关的警告,而不是缩小:

warning: extended initializer lists only available with -std=c++11 or -std=gnu++11

上面的行为似乎有问题,g++4.9不应该编译代码,g++5如果你忘记指定-std=c++11. 这是一个已知问题吗?

4

3 回答 3

9

里面的窄化转换{}只是C++11模式的错误,原因很简单:不是C++03的错误。现在,T var{value};是新的 C++11 语法,但T var = {value};已经是有效的 C++03 语法,并且确实允许缩小转换范围。

int i = { 10.1 }; // valid C++03, invalid C++11

它使 GCC 开发人员更容易在T var{value};T var={value};初始化中处理缩小转换。这很有用,因为它避免了编译器中警告的两个单独的代码路径。

它使 GCC 开发人员甚至可以更容易地接受T var{value};C++03 模式下的语法,而只是警告它。在 C++03 模式下还启用了其他几个 C++11 语法扩展。这很有用,因为在 GCC 的标准库实现中使用了几个 C++11 语法扩展(关于它的警告被禁止)。

int i{10.1}; 在 C++11 模式下在 GCC 4.9 中不是错误,但在 GCC 5 中出现错误的原因是因为不将其视为错误导致有效代码被拒绝。C++ 标准要求在 SFINAE 上下文中将其视为错误,这是一个有效的 C++11 程序,由于 GCC 4.9 的原因,它运行不正确:

#include <stdio.h>
template <typename T> void f(double) { puts("ok"); }
template <typename T, typename = decltype(T{10.1})> void f(int) { puts("error"); }
int main() { f<int>(1); }

这应该打印“ok”。第二个重载应该被丢弃。

使用 GCC 4.9,它会打印“错误”,因为第二个重载没有被丢弃,并且intdouble.

于 2015-02-11T23:25:02.213 回答
6

该标准从不说任何“不应该编译” (除了#error。某些格式不正确的程序必须发出诊断并发出警告才能满足此要求。

您可以使用 switch 使 gcc 停止所有诊断的编译-Werror。它可以缩小到特定的警告,例如-Werror=narrowing

如果您使用 GNU++ 或任何默认模式而不是 C++11 进行编译,那么编译器可以做它喜欢的任何事情,包括毫无怨言地接受缩小转换。

参考:N3936 [intro.compliance]/2

  • 如果程序包含违反任何可诊断规则[...],则符合要求的实现应发出至少一个诊断消息。

  • 如果一个程序违反了不需要诊断的规则,则本国际标准对该程序的实现没有要求。

[defns.诊断]

诊断信息

属于实现输出消息的实现定义子集的消息

还要注意第一个要点,消息的数量或内容不需要与违规的数量或内容相对应。

该标准完全由编译器决定如何组织其错误和/或警告,附带条件是对于某些违规行为,它不能默默地忽略它。

于 2015-02-11T23:00:44.873 回答
2

引用自1.4 [intro.compliance]

一个符合规范的实现可以有扩展(包括额外的库函数),只要它们不改变任何格式良好的程序的行为。需要实现来诊断使用根据本国际标准格式错误的扩展的程序。然而,这样做之后,他们可以编译和执行这样的程序。

您的初始化示例的适用部分是8.5.4 [dcl.init.list]. 尤其是,

否则,如果初始化列表有一个类型为E的元素,并且T不是引用类型或其引用类型与E引用相关,则从该元素初始化对象或引用;如果需要缩小转换(见下文)将元素转换为T,则程序格式错误

附上例子

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

由于诊断的确切性质是实施指定的,因此观察到的两组行为都符合标准。

于 2015-02-11T23:17:37.560 回答