g++4.9 和 g++5 在初始化列表中缩小时的不同行为
g++4.9 and g++5 different behaviour when narrowing in initializing list
考虑这段代码:
#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 -std=c++11
编译,g++5 不会编译它。但是,如果 -std=c++11
被省略,那么即使 g++5
也会愉快地编译它,只给出与大括号初始化相关的警告,而不是缩小:
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=narrowing
.
如果您使用 GNU++ 或任何默认模式而不是 C++11 进行编译,那么编译器可以为所欲为,包括毫无怨言地接受缩小转换。
参考:N3936 [intro.compliance]/2
If a program contains a violation of any diagnosable rule [...], a conforming implementation shall issue at least one diagnostic message.
If a program contains a violation of a rule for which no diagnostic is required, this International Standard places no requirement on implementations with respect to that program.
[defns.diagnostic]
diagnostic message
message belonging to an implementation-defined subset of the implementation’s output messages
另请注意第一个要点,即不要求消息的数量或内容与违规的数量或内容相对应。
该标准完全由编译器决定如何组织其错误 and/or 警告,条件是对于某些违规行为,它不能默默地忽略它。
引用自1.4 [intro.compliance]
A conforming implementation may have extensions (including additional
library functions), provided they do not alter the behavior of any
well-formed program. Implementations are required to diagnose programs
that use such extensions that are ill-formed according to this
International Standard. Having done so, however, they can compile and
execute such programs.
您的初始化示例的适用部分是 8.5.4 [dcl.init.list]
。特别是,
Otherwise, if the initializer list has a single element of type E and
either T is not a reference type or its referenced type is
reference-related to E , the object or reference is initialized from
that element; if a narrowing conversion (see below) is required to
convert the element to T , the program is ill-formed.
附上例子
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 开发人员更容易接受 C++03 模式下的 T var{value};
语法,只是对其发出警告。在 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,它打印 "error",因为第二个重载未被丢弃,并且 int
比 double
更匹配。
考虑这段代码:
#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 -std=c++11
编译,g++5 不会编译它。但是,如果 -std=c++11
被省略,那么即使 g++5
也会愉快地编译它,只给出与大括号初始化相关的警告,而不是缩小:
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=narrowing
.
如果您使用 GNU++ 或任何默认模式而不是 C++11 进行编译,那么编译器可以为所欲为,包括毫无怨言地接受缩小转换。
参考:N3936 [intro.compliance]/2
If a program contains a violation of any diagnosable rule [...], a conforming implementation shall issue at least one diagnostic message.
If a program contains a violation of a rule for which no diagnostic is required, this International Standard places no requirement on implementations with respect to that program.
[defns.diagnostic]
diagnostic message
message belonging to an implementation-defined subset of the implementation’s output messages
另请注意第一个要点,即不要求消息的数量或内容与违规的数量或内容相对应。
该标准完全由编译器决定如何组织其错误 and/or 警告,条件是对于某些违规行为,它不能默默地忽略它。
引用自1.4 [intro.compliance]
A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.
您的初始化示例的适用部分是 8.5.4 [dcl.init.list]
。特别是,
Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E , the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T , the program is ill-formed.
附上例子
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 开发人员更容易接受 C++03 模式下的 T var{value};
语法,只是对其发出警告。在 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,它打印 "error",因为第二个重载未被丢弃,并且 int
比 double
更匹配。