使用空初始化列表直接初始化

时间:2018-02-05 07:33:03

标签: c++ c++11 language-lawyer initializer-list

struct X
{
    X() { std::cout << "default ctor" << std::endl; }

};

int main()
{
    X({});
}

打印出来

default ctor

这是有道理的,因为空括号值初始化对象(我认为)。 然而,

struct X
{
    X() { std::cout << "default ctor" << std::endl; }
    X(std::initializer_list<int>) { std::cout << "list initialization" << std::endl; }
};

int main()
{
    X({});
}

为此,我得到了

initializer list

我没有发现这种行为如此奇怪,但我并不完全相信。对此有什么规定?

这种行为是否写在标准的某些部分?

2 个答案:

答案 0 :(得分:10)

要查看实际情况,请声明复制和移动构造函数,在C ++ 14模式或更早版本中进行编译,并禁用复制省略。

Coliru link

输出:

default ctor
move ctor

在第一个片段中,编译器查找带有单个参数的X构造函数,因为您提供了一个参数。这些是复制和移动构造函数X::X(const X&)X::X(X&&),如果您不自己声明它们,编译器将隐式声明这些构造函数。然后,编译器使用默认构造函数将{}转换为X对象,并将该X对象传递给移动构造函数。 (您必须使用fno-elide-constructors来查看此内容,否则编译器将忽略此移动,并且在C ++ 17中,复制省略变为强制性。)

在第二个代码段中,编译器现在可以选择将{}转换为X(然后调用移动构造函数),或将{}转换为std::initializer_list<int>(然后调用初始化列表构造函数)。根据[over.ics.list] /6.2,调用默认构造函数的{}X的转换是用户定义的转换,而根据[over.ics.list] / 4,从{}std::initializer_list<int>的转换是身份转换。标识转换优于用户定义的转换,因此编译器调用初始化列表构造函数。

答案 1 :(得分:7)

  

这种行为是否写在标准的某些部分?

当然。这完全由[dcl.init]/16中的规则决定,强调我的匹配你的初始值:

  

初始化器的语义如下。目的地类型是   正在初始化的对象或引用的类型以及源   type是初始化表达式的类型。如果初始化程序是   不是单个(可能带括号的)表达式,源类型是   没有定义。

     
      
  • 如果初始化程序是(非括号)braced-init-list,则对象或引用是列表初始化的([dcl.init.list])。

  •   
  • [...]

  •   
  • 如果目标类型是(可能是cv限定的)类类型:

         
        
    • 如果初始化是直接初始化,或者它是复制初始化,其中cv-nonqualified版本的源   type与类的类相同,或者是类的派生类   目的地,构造函数被视为。适用的构造函数   枚举([over.match.ctor]),并选择最好的一个   重载决议([over.match])。这样选择的构造函数是   调用初始化对象,使用初始化表达式或   表达式列表作为其参数。如果没有构造函数适用,或者   重载分辨率不明确,初始化是不正确的。
    •   
    • [...]
    •   
  •   

您提供带括号的空brace-init-list,因此只应用后一个项目符号。考虑构造函数,在第一种情况下,我们最终从默认的初始化X进行复制初始化。在后一种情况下,选择initializer_list c'tor作为更好的匹配。选择此重载的规则在[over.ics.list]

中指定
  

当参数是初始化列表([dcl.init.list])时,它不是   表达式和特殊规则适用于将其转换为参数   类型。

     

如果参数类型是std :: initializer_list或“X的数组”和   可以隐式转换初始化列表的所有元素   对于X,隐式转换序列是最差的转换   将列表元素转换为X所必需的。此转换可以   即使在调用的上下文中,也是用户定义的转换   初始化列表构造函数。

     

否则,如果参数是非聚合类X并且过载   每[over.match.list]的分辨率选择一个最好的构造函数   X从中执行X类型对象的初始化   参数初始化列表,隐式转换序列是一个   用户定义的转换序列。如果多个构造函数是可行的   但没有一个比其他更好,隐式转换序列   是模糊的转换序列。用户定义的转换是   允许将初始化列表元素转换为   构造函数参数类型,除了[over.best.ics]中所述。