我鼓励这个问题:如果我有
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} 时,所有编译都正确。如果我声明默认构造函数,它也会编译。为什么会这样?
我鼓励这个问题:如果我有
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} 时,所有编译都正确。如果我声明默认构造函数,它也会编译。为什么会这样?
该类是一个聚合,因此列表初始化将执行聚合初始化,并且不会考虑隐式声明的构造函数。
由于没有数据成员,因此只有空列表可以是有效的聚合初始化器。
但是当我使用
A b(a)
而不是A b{a}
全部正确编译时。
直接初始化将使用隐式构造函数,而不是尝试聚合初始化。
如果我声明默认构造函数,它也会编译。
通过声明构造函数,类不再是聚合,只能使用构造函数进行初始化。
当不定义自己的构造函数 class A
时,将被视为聚合(即plain-old-data)存储类型。
在处理聚合时,列表初始化不会考虑任何隐式声明的构造函数,而是会尝试直接初始化对象的成员。
在A b { a }
where A
is 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) 来转换该表达式,则程序是非良构的。
GCC 遵循标准,但这是一个已知缺陷,请参阅核心问题 1467。缺陷报告已于 2014 年 11 月解决,新行为在 GCC 的下一个主要版本(版本 5.1,2015 年 4 月发布)中得到支持。