当我运行此代码时:
struct X {
int a;
};
struct Y : public X {};
X x = {0};
Y Y = {0};
我明白了:
error: could not convert ‘{0}’ from ‘<brace-enclosed initializer list>’ to ‘Y’
为什么大括号初始化适用于基类而不适用于派生类?
答案 0 :(得分:45)
您的问题与aggregate initialization有关:struct X
是聚合,struct Y
则不是。以下是关于聚合的标准报价(8.5.1):
聚合是一个数组或类(第9节),没有用户提供的构造函数(12.1),没有用于非静态数据成员(9.2)的大括号或等于初始值,没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),没有虚函数(10.3)。
此子句指定如果class
具有基类,则它不是聚合。这里,struct Y
有struct X
作为基类,因此不能是聚合类型。
关于您遇到的特殊问题,请参阅标准中的以下条款:
当初始化程序列表初始化聚合时,如8.5.4中所述,初始化程序列表的元素将作为聚合成员的初始化程序,增加下标或成员顺序。每个成员都是从相应的initializer子句复制初始化的。如果initializer子句是一个表达式,并且转换表达式需要缩小转换(8.5.4),则程序格式不正确。
执行X x = {0}
时,会使用聚合初始化将a
初始化为0
。但是,当您执行Y y = {0}
时,由于struct Y
不是聚合类型,编译器将查找适当的构造函数。由于没有任何隐含生成的构造函数(默认,复制和移动)可以使用单个整数执行任何操作,因此编译器会拒绝您的代码。
关于这个构造函数查找,来自clang ++的错误消息对于编译器实际尝试做什么(online example)更加明确:
Y Y = {0};
^ ~~~
main.cpp:5:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const Y &' for 1st argument
struct Y : public X {};
^
main.cpp:5:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 'Y &&' for 1st argument
struct Y : public X {};
^
main.cpp:5:8: note: candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided
请注意,有a proposal扩展聚合初始化以支持您的用例,和使其成为C ++ 17。如果我正确地阅读它,它会使您的示例与您期望的语义一致。所以......你只需要等待符合C ++ 17标准的编译器。
答案 1 :(得分:0)
您现在的代码为compiles(GodBolt)。
您会收到有关使用brance的警告。因此,推荐的初始化Y
的方法是:
Y y = { {0} };
而不是;
Y y = { 0 };
这是因为现在将此类继承结构视为“聚合类型”(允许进行聚合初始化)。请参见2015年的this paper。