9

以下是来自Effective Modern C++(第 55 页)的引述:

“假设您使用一组空大括号来构造一个支持默认构造函数并且还支持 std::initializer_list 构造的对象。您的空大括号是什么意思?等等。规则是您获得默认构造。”

我用 std::array 试过这个:

std::array<int, 10> arr{};

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

警告:成员 'std::array<int, 10ul>::_M_elems'</p> 缺少初始化程序

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

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

4

1 回答 1

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
    

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

  • 否则,如果 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 };

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

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

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

我们又回到了8.5.4我们开始的部分。

于 2015-07-07T20:18:13.220 回答