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



考虑以下代码:

#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不应该编译代码,如果您忘记指定-std=c++11g++5编译代码会非常奇怪。这是已知的问题吗?

{}内部的缩小转换只是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开发人员在C++03模式下更容易接受T var{value};语法,而只是对此发出警告。其他几个C++11语法扩展也在C++0.3模式下启用。这是有用的,因为在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,它打印"error",因为第二个重载没有被丢弃,并且intdouble更匹配。

标准从不说任何话"不应编译"#error除外)。某些格式错误的程序必须发出诊断,并且发出警告满足这一要求。

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

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

参考:N3936[介绍合规性]/2

  • 如果程序违反了任何可诊断规则[…],则一致性实施应至少发布一条诊断消息。

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

[defns.diagnostic]

诊断消息

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

还请注意,从第一个要点来看,不要求消息的数量或内容与违规的数量或属性相对应。

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

1.4 [intro.compliance] 报价

一致性实施可能具有扩展(包括附加库函数),前提是它们不会改变任何格式良好的程序。诊断程序需要实施使用这种不正确的扩展国际标准。然而,这样做后,他们可以编译执行这样的程序。

初始化示例的适用部分是8.5.4 [dcl.init.list]。特别是

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

附有示例

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

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

相关内容

  • 没有找到相关文章

最新更新