我鼓励这个问题: 如果我有
class A
{
public:
};
int main()
{
A a{};
A b{a};
}
gcc给出:
moves.cc:在函数'int main()'中: moves.cc:15:7:错误:'A'的初始化程序太多 A b {a};
但是当我使用A b(a)代替A b {a}时,所有编译都正确。如果我声明默认构造函数,它也会编译。为什么会这样呢?
答案 0 :(得分:14)
该类是聚合,因此列表初始化将执行聚合初始化,并且不会考虑隐式声明的构造函数。
由于没有数据成员,因此只有空列表才能成为有效的聚合初始化程序。
但是当我使用
A b(a)
代替A b{a}
时,所有编译都正确。
直接初始化将使用隐式构造函数而不是尝试聚合初始化。
如果我声明默认构造函数,它也会编译。
通过声明构造函数,该类不再是聚合,只能使用构造函数初始化。
答案 1 :(得分:9)
如果没有定义自己的构造函数 class A
将被视为聚合(即普通旧数据)存储类型。
在处理聚合时, list-initialization 不会考虑任何隐式声明的构造函数,而是会尝试直接初始化对象的成员。
如果A b { a }
A
是聚合,编译器将尝试使用A
的值初始化a
中的第一个成员;这当然会失败,因为A
不包含任何成员。
<强>
[8.5.1 Aggregates]
强>
1)聚合是一个没有用户提供的数组或类(第9条) 构造函数(12.1),没有用于非静态的大括号或等于初始化程序 数据成员(9.2),没有私有或受保护的非静态数据成员 (第11条),没有基类(第10条),也没有虚函数 (10.3)。
2)当初始化列表初始化聚合时,如 在8.5.4中指定,初始化器的元素被视为 增加下标中聚合成员的初始值设定项 或会员订单。每个成员都是从中复制初始化的 相应的初始化子句。如果initializer-clause是 转换需要表达式和缩小转换(8.5.4) 表达方式,该程序是不正确的。
答案 2 :(得分:2)
GCC遵循标准,但这是一个已知的缺陷,请参阅core issue 1467。缺陷报告于2014年11月得到解决,GCC的下一个主要版本(2015年4月发布的5.1版)支持新行为。