9

我鼓励这个问题:如果我有

class A
{
public:
};
int main()
{
   A a{};
   A b{a};
}

gcc 给出:

move.cc:在函数'int main()'中:moves.cc:15:7:错误:'A'A b {a}的初始化程序太多;

但是当我使用 A b(a) 而不是 A b{a} 时,所有编译都正确。如果我声明默认构造函数,它也会编译。为什么会这样?

4

3 回答 3

14

该类是一个聚合,因此列表初始化将执行聚合初始化,并且不会考虑隐式声明的构造函数。

由于没有数据成员,因此只有空列表可以是有效的聚合初始化器。

但是当我使用A b(a)而不是A b{a}全部正确编译时。

直接初始化将使用隐式构造函数,而不是尝试聚合初始化。

如果我声明默认构造函数,它也会编译。

通过声明构造函数,类不再是聚合,只能使用构造函数进行初始化。

于 2013-10-01T09:25:08.653 回答
9

当不定义自己的构造函数 class A时,将被视为聚合(即plain-old-data)存储类型。

在处理聚合时,列表初始化不会考虑任何隐式声明的构造函数,而是会尝试直接初始化对象的成员。

A b { a }where Ais a aggregate 的情况下,编译器将尝试使用 ;A的值初始化第一个成员a。这当然会失败,因为A不包含任何成员。


标准是怎么说的?

[8.5.1 Aggregates]

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

2)当聚合由初始化列表初始化时,如 8.5.4 中所指定,初始化的元素被视为聚合成员的初始化,按递增的下标或成员顺序。每个成员都是从相应的 initializer-clause 复制初始化的。如果 initializer-clause 是一个表达式并且需要一个窄化转换 (8.5.4) 来转换该表达式,则程序是非良构的。

于 2013-10-01T09:27:03.277 回答
2

GCC 遵循标准,但这是一个已知缺陷,请参阅核心问题 1467。缺陷报告已于 2014 年 11 月解决,新行为在 GCC 的下一个主要版本(版本 5.1,2015 年 4 月发布)中得到支持。

于 2013-10-01T12:26:36.640 回答