为什么我不能用括号来初始化一个来自另一个结构的结构?

时间:2022-09-30 03:13:10

When I run this code:

当我运行这段代码:

struct X {
    int a;
};

struct Y : public X {};

X x = {0};
Y Y = {0};

I get:

我得到:

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?

为什么括号初始化对基类有效,而对派生类无效?

1 个解决方案

#1


39  

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):

您的问题与聚合初始化有关:struct X是聚合,而struct Y不是。以下是关于聚合的标准报价(8.5.1):

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).

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

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.

这个子句指定如果一个类有一个基类,那么它不是一个聚合。在这里,struct Y有struct X作为基类,因此不能是聚合类型。

Concerning the particular problem you have, take the following clause from the standard:

关于您所遇到的特殊问题,请参考标准中的下列条款:

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.

当初始化器列表初始化一个聚合时(如8.5.4中所指定的),初始化器列表的元素作为聚合成员的初始化器,以增加下标或成员顺序。每个成员都由相应的initializer-子句进行复制初始化。如果initializer-子句是一个表达式,并且需要一个收缩转换(8.5.4)来转换表达式,那么这个程序是不成形的。

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.

当X ={0}时,使用聚合初始化将a初始化为0。但是,当您做Y Y ={0}时,因为struct Y不是一个聚合类型,编译器会寻找合适的构造函数。由于没有任何隐式生成的构造函数(默认、复制和移动)可以使用单个整数进行任何操作,因此编译器会拒绝您的代码。


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):

关于这个构造函数查找,来自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

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.

注意,有一个扩展聚合初始化以支持您的用例的建议,并将其转换为c++ 17。如果我正确地阅读了它,它将使您的示例具有您所期望的语义。所以…您只需要等待一个c++ 17兼容的编译器。

#1


39  

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):

您的问题与聚合初始化有关:struct X是聚合,而struct Y不是。以下是关于聚合的标准报价(8.5.1):

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).

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

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.

这个子句指定如果一个类有一个基类,那么它不是一个聚合。在这里,struct Y有struct X作为基类,因此不能是聚合类型。

Concerning the particular problem you have, take the following clause from the standard:

关于您所遇到的特殊问题,请参考标准中的下列条款:

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.

当初始化器列表初始化一个聚合时(如8.5.4中所指定的),初始化器列表的元素作为聚合成员的初始化器,以增加下标或成员顺序。每个成员都由相应的initializer-子句进行复制初始化。如果initializer-子句是一个表达式,并且需要一个收缩转换(8.5.4)来转换表达式,那么这个程序是不成形的。

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.

当X ={0}时,使用聚合初始化将a初始化为0。但是,当您做Y Y ={0}时,因为struct Y不是一个聚合类型,编译器会寻找合适的构造函数。由于没有任何隐式生成的构造函数(默认、复制和移动)可以使用单个整数进行任何操作,因此编译器会拒绝您的代码。


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):

关于这个构造函数查找,来自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

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.

注意,有一个扩展聚合初始化以支持您的用例的建议,并将其转换为c++ 17。如果我正确地阅读了它,它将使您的示例具有您所期望的语义。所以…您只需要等待一个c++ 17兼容的编译器。