为什么我不能括起来初始化从另一个结构派生的结构?

时间:2013-06-07 11:55:14

标签: c++ c++11 struct list-initialization

当我运行此代码时:

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’

为什么大括号初始化适用于基类而不适用于派生类?

2 个答案:

答案 0 :(得分:45)

您的问题与aggregate initialization有关:struct X是聚合,struct Y则不是。以下是关于聚合的标准报价(8.5.1):

  

聚合是一个数组或类(第9节),没有用户提供的构造函数(12.1),没有用于非静态数据成员(9.2)的大括号或等于初始值,没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),没有虚函数(10.3)。

此子句指定如果class具有基类,则它不是聚合。这里,struct Ystruct 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)

使用C ++ 17和更高版本,您可以 大括号初始化您的派生结构。

您现在的代码为compiles(GodBolt)。

您会收到有关使用brance的警告。因此,推荐的初始化Y的方法是:

Y y = { {0} };

而不是;

Y y = { 0 };

这是因为现在将此类继承结构视为“聚合类型”(允许进行聚合初始化)。请参见2015年的this paper