std::initializer_list 构造器

std::initializer_list constructor

在这样的代码中:

#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 以数字作为模板参数如此具体?

{}到标量类型(如intdoublechar*等)就是恒等转换。

{} 到 class 类型而不是 std::initializer_list 的特化(例如,std::string)是用户定义的转换。

前者胜过后者

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

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

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

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

Otherwise, if the parameter type is std​::​initializer_­list and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X, or if the initializer list has no elements, the identity conversion. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor.

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

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

  • if the initializer list has one element that is not itself an initializer list, the implicit conversion sequence is the one required to convert the element to the parameter type
  • ...
  • In all cases other than those enumerated above, no conversion is possible.

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


A a3({})的不同之处在于,在这种情况下,它不是列表初始化,而是带有{}参数的"normal"初始化(注意少了一个嵌套,因为缺少外括号)。这里我们的两个 f 函数是用 {} 调用的。并且由于两个列表都没有元素,因此我们都有身份转换,因此存在歧义。

本例中的编译器也会考虑 f(int) 并与其他两个函数并列。但是决胜局将适用于声明 int 参数比 initializer_list 参数差。所以你有一个偏序{int} < {initializer_list<string>, initializer_list<int>},这是歧义的原因,因为最好的一组转换序列不包含一个候选,而是两个。