代码声明Foo a2[2];
声明一个数组。初始化数组的唯一方法是通过列表初始化(即用大括号括起来的零个或多个元素的列表),并且该行为由标题为“标准”的部分描述聚合初始化 http://en.cppreference.com/w/cpp/language/aggregate_initialization。 (期限总计的指的是数组和满足特定条件的类)。
在聚合初始化中,存在=
没有什么区别。它的基本定义在C++14 [dcl.init.aggr]/2中:
当聚合由初始值设定项列表初始化时,如 8.5.4 中所指定,初始值设定项列表的元素将被视为聚合成员的初始值设定项,按递增的下标或成员顺序。每个成员都是从相应的初始化子句复制初始化的。
另外,/7:
如果列表中的初始化子句少于聚合中的成员,则每个未显式初始化的成员应从其大括号或等于初始化器初始化,或者如果没有大括号或等于初始化器,则应从其大括号或等于初始化器初始化。
初始化器,来自空初始化器列表(8.5.4)。
您可以从中看到,复制初始化始终用于每个提供的初始值设定项。因此,当初始值设定项是表达式时,该类必须存在可访问的复制/移动构造函数。
但是(按照 Anty 的建议)您可以将初始化器设置为另一个列表。使用列表的复制初始化称为复制列表初始化:
Foo a6[2] = {{6}, {6}};
当一个人Foo
是列表初始化的,它不是聚合初始化(因为Foo
不是一个集合)。因此,规则与上面讨论的规则不同。非聚合类的复制列表初始化属于列表初始化,在 [dcl.init.list]/3.4 中,它指定Foo
使用重载解析将列表中的初始值设定项与构造函数参数进行匹配。在这个阶段Foo(int)
将选择构造函数,这意味着不需要复制构造函数。
为了完整起见,我将提到核选择 https://stackoverflow.com/questions/28187732/placement-new-in-stdaligned-storage:
typename std::aligned_storage< sizeof(Foo), alignof(Foo) >::type buf[2];
::new ((void *)::std::addressof(buf[0])) Foo(5);
::new ((void *)::std::addressof(buf[1])) Foo(5);
Foo *a7 = reinterpret_cast<Foo *>(buf);
// ...
a7[0].~Foo();
a7[1].~Foo();
显然,当您无法通过任何其他方式实现目标时,这是最后的手段。
注1:以上适用于C++14。在 C++17 中,我相信所谓的“保证复制省略”会将复制初始化更改为实际上不需要复制/移动构造函数。一旦标准发布,我希望更新这个答案。草案中还对聚合初始化进行了一些修改。