C++ g++;4.9和g++;5在初始化列表中缩小范围时的不同行为

C++ g++;4.9和g++;5在初始化列表中缩小范围时的不同行为,c++,c++11,gcc4.9,gcc5,C++,C++11,Gcc4.9,Gcc5,考虑以下代码: #include <iostream> int main() { int i{10.1}; // narrowing, should not compile std::cout << i << std::endl; } 删除-std=c++11标志会导致关于大括号初始的警告,但不会导致任何变窄: warning: extended initializer lists only available with -std=c++1

考虑以下代码:

#include <iostream>

int main()
{
    int i{10.1}; // narrowing, should not compile
    std::cout << i << std::endl;
}
删除
-std=c++11
标志会导致关于大括号初始的警告,但不会导致任何变窄:

warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
warning: extended initializer lists only available with -std=c++11 or -std=gnu++11
另一方面,如果使用
g++5-std=c++11
进行编译,g++5不会编译它。但是,如果省略了
-std=c++11
,则即使是
g++5
也会愉快地编译它,只给出一个与大括号init相关的警告,而不是与收缩相关的警告:

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

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

该标准从未说过任何“不应编译”(除了
#error
)。某些格式错误的程序必须发出诊断并发出警告

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

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

参考编号:N3936[合规性简介]/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

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

内部的窄化转换只在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++11语法扩展。这是很有用的,因为在GCC的标准库实现中使用了几个C++11语法扩展(其中有关它的警告被抑制)

inti{10.1}的原因
在C++11模式下的GCC4.9中不是错误,但在GCC5中是错误的,因为不将其视为错误会导致拒绝有效代码。C++标准要求在SfAEAE上下文中将其视为一个错误,这里是一个有效的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); }
#包括
模板void f(double){puts(“ok”);}
模板void f(int){puts(“error”);}
int main(){f(1);}
这应该是打印“ok”。第二个重载应该被丢弃


在GCC4.9中,它打印“error”,因为第二个重载没有被丢弃,
int
double

@MattMcNabb更匹配,即使是这样,在没有-std=c++11的情况下编译时,g++5至少应该发出一个警告,而让我困惑的是,即使在stroostroup页面上,他也谈到了“如何防止变窄”。依我看,仅仅发出警告并不能阻止任何事情。这与int foo=1.0f相同,它可以在任何地方编译,但如果级别足够高(msvc上为4级)@paulm,则会生成一个警告。这其实并不相同。这是一个“品质”大括号初始化的关键是防止狭窄。但看起来编译器只是需要发出警告。在上面的链接上,B.Stroustroup甚至说:“然而,在C++11中,{}初始化并没有缩小:…”谢谢。让我困惑的事实是,如果查看标准,您会看到标有
//error
注释的狭窄声明,例如
int&r;//错误
。现在,常识告诉我,当你用
错误
注释某个东西时,它应该