b
是一个聚合体。当你使用初始化列表对其进行初始化时,列表中的元素将初始化聚合体的前n个成员,其中n是列表中元素的数量。聚合体的剩余元素将进行复制列表初始化。
因此,在您的示例中,c
将进行复制列表初始化,但如果选择的构造函数是explicit
,则会出现错误。
相关的标准引用如下:
[dcl.init.aggr]/3
当使用[dcl.init.list]中指定的初始化器列表初始化聚合体时,初始化器列表的元素将作为聚合体元素的初始化器。聚合体的显式初始化元素如下确定:
...
- 如果初始化器列表是一个初始化器列表,则聚合体的显式初始化元素是聚合体的前n个元素,其中n是初始化器列表中元素的数量。
- 否则,初始化器列表必须为{},并且没有显式初始化元素。
[dcl.init.aggr]/5
对于非联合聚合体,每个未显式初始化的元素的初始化如下:
...
- 否则,如果该元素不是引用,则从空初始化列表[dcl.init.list]复制初始化该元素。
从空初始化列表[dcl.init.list]中复制初始化的效果如下:
[dcl.init.list]/3
对于类型为
T
的对象或引用的列表初始化定义如下:
...
- 否则,如果初始化程序列表没有元素且
T
是具有默认构造函数的类类型,则该对象将进行值初始化。
[dcl.init]/8
对于类型为
T
的对象进行
值初始化的意思是:
...
- 如果
T
是一个(可能带有cv限定符的)类类型,它没有默认构造函数([class.ctor])或者该默认构造函数是用户提供的或已删除,则该对象将进行默认初始化;
[dcl.init]/7
对于类型T进行默认初始化意味着:
- 如果T是一个(可能带有cv限定符的)类类型,则会考虑构造函数。适用的构造函数将被枚举([over.match.ctor]),并通过重载分辨选择最佳的初始化器()。因此,所选的构造函数将被调用,使用空参数列表来初始化对象。
[over.match.ctor]
对于复制初始化,候选函数是该类的所有转换构造函数。
[class.conv.ctor]/1
一个没有使用
function-specifier explicit
声明的构造函数,指定了从其参数类型(如果有)到其类类型的转换。这样的构造函数被称为
转换构造函数。
在上面的示例中,
a
没有转换构造函数,因此重载解析失败。在
[class.conv.ctor]/2中的(非规范性的)示例甚至包含了一个非常相似的情况。
struct Z {
explicit Z();
explicit Z(int);
explicit Z(int, int);
};
Z c = {};
您可以通过为c
提供默认成员初始化器来避免错误。
struct b
{
a c{};
};
b d;
或者b d{a{}}
同样可以工作。 - Yi Tang