缩小初始化列表时g ++ 4.9和g ++ 5的不同行为

vso*_*tco 3 c++ c++11 gcc4.9 gcc5

考虑以下代码:

#include <iostream>

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

根据C++ 11标准,它不应该编译(禁止在大括号初始化中缩小.)

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

warning: narrowing conversion of '1.01e+1' from 'double' to 'int' inside { } [-Wnarrowing]
Run Code Online (Sandbox Code Playgroud)

删除-std=c++11标志会导致关于大括号init的警告,但不会导致任何缩小:

warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
Run Code Online (Sandbox Code Playgroud)

另一方面,如果你编译,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
Run Code Online (Sandbox Code Playgroud)

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

小智 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
Run Code Online (Sandbox Code Playgroud)

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

它使GCC开发人员更容易接受T var{value};C++ 03模式中的语法,只是警告它.在C++ 03模式下也启用了其他几个C++ 11语法扩展.这很有用,因为在GCC的标准库实现中使用了几个C++ 11语法扩展(其中有关它的警告被抑制).

其原因int i{10.1}; 是没有在GCC 4.9在C++ 11模式的误差,但在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); }
Run Code Online (Sandbox Code Playgroud)

这应该打印"ok".应该丢弃第二个重载.

使用GCC 4.9,它会打印"错误",因为第二个重载不会被丢弃,并且int是一个更好的匹配double.


M.M*_*M.M 6

标准从不说"不应该编译" (除了#error).某些不正确的程序必须发出诊断并发出警告以满足这一要求.

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

如果您使用GNU ++进行编译或者使用默认模式而不是C++ 11编译,则编译器可以执行任何喜欢的操作,包括接受缩小的转换而无需投诉.

参考:N3936 [intro.compliance]/2

  • 如果程序包含违反任何可诊断规则的情况,则符合要求的实施方案应至少发出一条诊断消息.

  • 如果程序包含违反不需要诊断的规则,则本国际标准不要求对该程序的实施.

[defns.diagnostic]

诊断信息

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

另请注意,从第一点开始,不要求消息的数量或内容与违规的数量或内容相对应.

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

  • 与C不同,C++标准甚至没有说'#error`不应该编译.参见http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#745 (2认同)