Pra*_*han 11 c++ overload-resolution c++11 list-initialization
以下内容无法编译clang35 -std=c++11:
#include <iostream>
#include <string>
#include <initializer_list>
class A
{
public:
A(int, bool) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
A(int, double) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
A(std::initializer_list<int>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
int main()
{
A a1 = {1, 1.0};
return 0;
}
Run Code Online (Sandbox Code Playgroud)
有错误
init.cpp:15:14: error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
A a1 = {1, 1.0};
^~~
init.cpp:15:14: note: insert an explicit cast to silence this issue
A a1 = {1, 1.0};
^~~
static_cast<int>( )
Run Code Online (Sandbox Code Playgroud)
OTOH,它警告缩小和编译 g++48 -std=c++11
init.cpp: In function ‘int main()’:
init.cpp:15:17: warning: narrowing conversion of ‘1.0e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
A a1 = {1, 1.0};
^
init.cpp:15:17: warning: narrowing conversion of ‘1.0e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
Run Code Online (Sandbox Code Playgroud)
并产生结果
A::A(std::initializer_list<int>)
Run Code Online (Sandbox Code Playgroud)
这两种行为都有意义吗?引用cppreference
将std :: initializer_list作为唯一参数的所有构造函数,或者作为第一个参数,如果其余参数具有默认值,将检查所有构造函数,并通过重载决策与std :: initializer_list类型的单个参数进行匹配
如果前一个阶段没有产生匹配,则T的所有构造函数都参与对由braced-init-list元素组成的参数集的重载解析,并限制只允许非缩小转换.如果此阶段产生显式构造函数作为复制列表初始化的最佳匹配,则编译失败(注意,在简单的复制初始化中,根本不考虑显式构造函数)
由于不允许缩小转换,我希望重载解析步骤与A(std::initializer_list<int>)构造函数不匹配,而是匹配构造函数A(int, double).例如,更改A(std::initializer_list<int>)为A(std::initializer_list<std::string>)使用clang35和编译g++48并打印
A::A(int, double)
Run Code Online (Sandbox Code Playgroud)
正如所料.
Bar*_*rry 11
这种行为是有道理的.Scott Meyers在Effective Modern C++中有一个与此类似的例子(强调原文):
但是,如果一个或多个构造函数声明了一个类型的参数
std::initializer_list,则使用支撑初始化语法的调用非常喜欢使用std;:initializer_lists 的重载.强烈.如果编译器有任何方法可以将使用支撑初始化器的调用解释为构造函数std::initializer_list,那么编译器将使用该解释.
使用此类的示例:
class Widget {
public:
Widget(int, bool);
Widget(int, double);
Widget(std::initializer_list<long double>);
};
Widget w1(10, true); // calls first ctor
Widget w2{10, true}; // calls std::initializer_list ctor
Widget w3(10, 5.0); // calls second ctor
Widget w4{10, 5.0}; // calls std::initializer_list ctor
Run Code Online (Sandbox Code Playgroud)
这两个调用调用initializer_listctor,即使它们涉及转换BOTH参数 - 即使其他构造函数是完美匹配.
此外:
编译器将支持的初始化器与构造函数匹配的决策
std::initializer_list非常强大,即使std::initializer_list无法调用最佳匹配构造函数,它也会占上风.例如:Run Code Online (Sandbox Code Playgroud)class Widget { public: Widget(int, bool); // as before Widget(int, double); // as before Widget(std::initializer_list<bool> ); // now bool }; Widget w{10, 5.0}; // error! requires narrowing conversions
两个编译器都选择了正确的重载(initializer_list一个) - 我们可以看到标准需要(第13.3.1.7节):
当非聚合类类型的对象
T被列表初始化(8.5.4)时,重载决策分两个阶段选择构造函数:(1.1) - 最初,候选函数是类的初始化列表构造函数(8.5.4),
T参数列表由初始化列表作为单个参数组成.
(1.2) - 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类的所有构造函数,T参数列表由初始化列表的元素组成.
但是调用那个特定的构造函数涉及到缩小范围.在8.5.1中:
如果initializer子句是表达式并且转换表达式需要缩小转换(8.5.4),则程序格式错误.
所以该计划是不正确的.在这种情况下,当gcc选择发出警告时,clang选择抛出错误.两个编译器都符合要求.
| 归档时间: |
|
| 查看次数: |
1181 次 |
| 最近记录: |