空括号调用默认构造函数或构造函数采用std :: initializer_list吗?

Alw*_*ing 8 c++ aggregate-initialization c++11

以下是Effective Modern C++(第55页)的引用:

"假设你使用一组空的大括号来构造一个支持默认构造函数的对象,并且还支持std :: initializer_list构造.你的空括号是什么意思?等规则是你得到默认构造."

我用std :: array尝试过这个:

std::array<int, 10> arr{};
Run Code Online (Sandbox Code Playgroud)

并得到了g ++(版本4.8.2)的警告:

警告:缺少成员'std :: array <int,10ul> :: _ M_elems'的初始值设定项

这是在尝试std::array从空构造一个时得到的警告std::initializer_list(请参阅为什么我可以从{}初始化一个常规数组,而不是std :: array来讨论此警告).

那么,为什么上面的代码行不能解释为调用默认构造函数?

Sha*_*our 8

这是因为std :: array是一个聚合,因此执行聚合初始化,这在草案C++ 11标准部分8.5.4 [dcl.init.list]有所说明:

列表初始化对象或类型T的引用定义如下:

  • 如果初始化列表没有元素且T是具有默认构造函数的类类型,则对象是值初始化的.

  • 否则,如果T是聚合,则执行聚合初始化(8.5.1).

    double ad[] = { 1, 2.0 }; // OK
    int ai[] = { 1, 2.0 }; // error: narrowing
    
    struct S2 {
      int m1;
      double m2, m3;
    };
    
    S2 s21 = { 1, 2, 3.0 }; // OK
    S2 s22 { 1.0, 2, 3 }; // error: narrowing
    S2 s23 { }; // OK: default to 0,0,0
    
    Run Code Online (Sandbox Code Playgroud)

我们可以看到它是不是聚合,然后列表继续说:

  • 否则,如果T是std :: initializer_list的特化,则如下所述构造initializer_list对象,并用于根据相同类型的类(8.5)中的对象初始化规则初始化对象.
  • 否则,如果T是类类型,则考虑构造函数.枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数.如果转换任何参数需要缩小转换(见下文),则程序格式错误.

我们可以确认std::array是来自23.3.2.1 [array.overview]部分的聚合:

数组是可以使用语法初始化的聚合(8.5.1)

array<T, N> a = { initializer-list };
Run Code Online (Sandbox Code Playgroud)

其中initializer-list是一个逗号分隔的列表,最多包含N个元素,其类型可转换为T.

8.5.1引用的部分是8.5.1Aggregates [dcl.init.aggr]并说:

当聚合由初始化列表初始化时,如8.5.4中所述,初始化列表的元素被视为聚合成员的初始化者,增加下标或成员顺序[...]

然后我们回到8.5.4我们开始的部分.