Eri*_*ric 37 c++ struct c++11 list-initialization
当我运行此代码时:
struct X {
int a;
};
struct Y : public X {};
X x = {0};
Y Y = {0};
Run Code Online (Sandbox Code Playgroud)
我明白了:
error: could not convert ‘{0}’ from ‘<brace-enclosed initializer list>’ to ‘Y’
Run Code Online (Sandbox Code Playgroud)
为什么大括号初始化适用于基类而不适用于派生类?
Mor*_*enn 47
您的问题与聚合初始化有关:struct X
是聚合而struct Y
不是聚合.以下是有关聚合的标准报价(8.5.1):
聚合是一个数组或类(第9节),没有用户提供的构造函数(12.1),非静态数据成员(9.2)没有大括号或等于初始值,没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),没有虚函数(10.3).
该子句指定如果a class
具有基类,则它不是聚合.在此,struct Y
具有struct X
作为基类,因此不能是聚合类型.
关于您遇到的特殊问题,请遵循以下标准:
当初始化程序列表初始化聚合时,如8.5.4中所述,初始化程序列表的元素将作为聚合成员的初始化程序,增加下标或成员顺序.每个成员都是从相应的initializer子句复制初始化的.如果initializer子句是表达式并且转换表达式需要缩小转换(8.5.4),则程序格式错误.
执行此操作时X x = {0}
,将使用聚合初始化初始化a
为0
.但是,当您这样做时Y y = {0}
,因为struct Y
它不是聚合类型,编译器将查找适当的构造函数.由于没有任何隐含生成的构造函数(默认,复制和移动)可以使用单个整数执行任何操作,因此编译器会拒绝您的代码.
关于这个构造函数查找,来自clang ++的错误消息更加明确了编译器实际上要做的事情(在线示例):
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
Run Code Online (Sandbox Code Playgroud)
请注意,有一个建议是扩展聚合初始化以支持您的用例,并将其转换为C++ 17.如果我正确地阅读它,它会使您的示例与您期望的语义一致.所以......你只需要等待符合C++ 17标准的编译器.
您的代码现在可以编译了(GodBolt)。
您确实会收到有关使用大括号的警告。因此,推荐的初始化 a 的方法Y
是:
Y y = { {0} };
Run Code Online (Sandbox Code Playgroud)
而不是;
Y y = { 0 };
Run Code Online (Sandbox Code Playgroud)
这是因为这种继承结构现在被视为“聚合类型”(支持这种类型的聚合初始化)。请参阅2015 年的这篇论文。