std :: initializer_list构造函数

too*_*zzy 12 c++ object-construction c++11 stdinitializerlist

在这样的代码中:

#include <iostream> 
#include <initializer_list>
#include <string>

struct A 
{
  A() { std::cout << "2" << std::endl; }
  A(int a) { std::cout << "0" << std::endl; }
  A(std::initializer_list<std::string> s) { std::cout << "3" << std::endl; }
  A(std::initializer_list<int> l) { std::cout << "1" << std::endl; } 
};

int main() 
{ 
 A a1{{}}; 
} 
Run Code Online (Sandbox Code Playgroud)

为什么调用std::initializer_list<int>构造函数的规范?如果我们定义构造函数,它会产生歧义编译错误std::initializer_list<double>.这种结构的规则是什么?为什么std::initializer_list数字作为模板参数如此具体?

T.C*_*.C. 7

{}为标量类型(如int,double,char*等)是单位转换.

{}除了std::initializer_list(例如std::string)的特化之外的类类型是用户定义的转换.

前者胜过后者.


Joh*_*itb 7

如果一个类有一个初始化列表构造函数,那么就{whatever goes here}意味着将{whatevergoeshere}参数作为参数传递给当前的构造函数(如果没有初始化列表构造函数,则whatever goes here作为参数传递).

因此,让我们简化设置并忽略其他构造函数,因为显然编译器并不关心它们

void f(std::initializer_list<std::string> s);
void f(std::initializer_list<int> l); 
Run Code Online (Sandbox Code Playgroud)

因为f({{}})我们有这个规则

否则,如果参数类型是std :: initializer_list并且初始化列表的所有元素都可以隐式转换为X,则隐式转换序列是将列表元素转换为X所需的最差转换,或者如果初始化列表没有元素,标识转换.即使在调用initializer-list构造函数的上下文中,此转换也可以是用户定义的转换.

这里我们有一个元素{},它需要用户定义的转换来初始化std::string而不需要转换(标识)int.因此,int被选中.

对于f({{{}}})元素是{{}}.可以转换成int?规则是

  • 如果初始化列表有一个本身不是初始化列表的元素,则隐式转换序列是将元素转换为参数类型所需的序列
  • ...
  • 在除上述列举之外的所有情况下,都不可能进行转换.

可以转换成std::string?是的,因为它有一个具有std::initializer_list<char> init参数的初始化列表构造函数.因此,std::string这次选择.


不同之处A a3({})在于,在这种情况下,它不是列表初始化,而是带有{}参数的"正常"初始化(请注意,由于缺少外部大括号而少了一个嵌套).这里我们的两个f功能被称为{}.由于两个列表都没有元素,因此我们都有身份转换,因此存在歧义.

在这种情况下,编译器也将考虑f(int)并与其他两个函数建立联系.但是应用一个int决胜论者宣称 - 参数比参数更差initializer_list.所以你有一个偏序{int} < {initializer_list<string>, initializer_list<int>},这是模糊性的原因,因为最好的一组转换序列不包含一个候选者,而是两个.