问题描述
当我运行此代码时:
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’
为什么花括号初始化对基类有效,而对派生类无效?
Why does brace initialization work for the base class but not the derived class?
推荐答案
针对C++17之前的C++标准版本的回答:
您的问题与聚合初始化有关:structX
是一个聚合,而 struct Y
不是.以下是关于聚合 (8.5.1) 的标准引用:
Your problem has to do with aggregate initialization: struct X
is an aggregate while struct Y
is not. Here is the standard quote about aggregates (8.5.1):
聚合是一个数组或类(第 9 条),没有用户提供的构造函数(12.1),没有用于非静态数据成员的大括号或等号初始化器(9.2),没有私有或受保护的非静态数据成员(第 11 条),没有基类(第 10 条),也没有虚函数(10.3).
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
这个子句指定如果一个 class
有一个基类,那么它不是一个聚合.这里,struct Y
有 struct X
作为基类,因此不能是聚合类型.
This clause specifies that if a class
has a base class, then it's not an aggregate. Here, struct Y
has struct X
as a base class and thus cannot be an aggregate type.
关于您遇到的特定问题,请从标准中提取以下条款:
Concerning the particular problem you have, take the following clause from the standard:
当一个聚合被初始化列表初始化时,如 8.5.4 中所指定的,初始化列表的元素被作为聚合成员的初始化,按照递增的下标或成员顺序.每个成员都是从相应的初始化子句复制初始化的.如果 initializer-clause 是一个表达式,并且需要进行收缩转换(8.5.4)来转换该表达式,则程序格式错误.
When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause. If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed.
当您执行X x = {0}
时,使用聚合初始化将a
初始化为0
.但是,当您执行 Y y = {0}
时,由于 struct Y
不是聚合类型,编译器将寻找合适的构造函数.由于隐式生成的构造函数(默认、复制和移动)都不能对单个整数执行任何操作,因此编译器会拒绝您的代码.
When you do X x = {0}
, aggregate initialization is used to initialize a
to 0
. However, when you do Y y = {0}
, since struct Y
is not an aggregate type, the compiler will look for an appropriate constructor. Since none of the implicitely generated constructors (default, copy and move) can do anything with a single integer, the compiler rejects your code.
关于这个构造函数查找,来自 clang++ 的错误消息对于编译器实际尝试执行的操作更加明确(在线示例):
Concerning this constructors lookup, the error messages from clang++ are a little bit more explicit about what the compiler is actually trying to do (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
注意有一个提案 扩展聚合初始化以支持您的用例, 将其纳入 C++17.如果我读对了,它会使您的示例在您期望的语义下有效.所以......你只需要等待一个符合 C++17 的编译器.
Note that there is a proposal to extend aggregate initialization to support your use case, and it made it into C++17. If I read it correctly, it makes your example valid with the semantics you expect. So... you only have to wait for a C++17-compliant compiler.
这篇关于为什么我不能大括号初始化从另一个结构派生的结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!