std :: initializer_list构造函数

时间:2018-01-29 08:05:04

标签: c++ c++11 object-construction 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{{}}; 
} 

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

2 个答案:

答案 0 :(得分:7)

{}标量类型(例如intdoublechar*等)是标识转换。

{}std::initializer_list的特殊化以外的类类型(例如,std::string)是用户定义的转化。

前者胜过后者。

答案 1 :(得分:7)

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

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

void f(std::initializer_list<std::string> s);
void f(std::initializer_list<int> l); 

对于f({{}}),我们有此规则

  

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

此处我们有一个元素{},它需要用户定义的转化来初始化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>},这是歧义的原因,因为最好的一组转换序列不包含一个候选者,而是两个。