c++ - 不可复制的类数据成员的统一初始化导致 gcc 错误
问题描述
假设我们有这样的代码:
class A {
public:
A() = default;
A(const A&) = delete;
~A() = default;
};
class B {
public:
B() : a{} { }
A a[1];
};
int main()
{
B b;
}
此代码在最新的 GCC 9.2、Clang 9.2 和 MSVC 19.22 上编译。
但是当我将 A default destructor 更改为~A() { }
GCC 时会返回 error use of deleted function 'A::A(const A&)'
。Clang 和 MSVC 仍然可以编译。
当我编写 的复制构造函数时A
,GCC 会编译,但在运行时从未调用过这个构造函数。GCC 需要复制构造函数做什么?是 GCC 错误吗?(我在 GodBolt.org 上尝试过所有 GCC 版本,同样的错误。)
解决方案
This is a GCC bug.
The default constructor of B
uses aggregate initialization to initialize a
with no initializers. [dcl.init.aggr]/8:
If there are fewer initializer-clauses in the list than there are elements in a non-union aggregate, then each element not explicitly initialized is initialized as follows:
If the element has a default member initializer ([class.mem]), the element is initialized from that initializer.
Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
Otherwise, the program is ill-formed.
[...]
So a[0]
is copy-initialized from {}
, which is copy-list-initialization. This is probably where GCC starts to get confused — copy-initialization does not necessarily involve the copy constructor.
List-initialization of an object or reference of type
T
is defined as follows:
[...]
Otherwise, if the initializer list has no elements and
T
is a class type with a default constructor, the object is value-initialized.[...]
Therefore, the default constructor of A
is used directly. There's no copy constructor involved. Nor triviality.
In case you are worrying about the difference between C++11 and C++17, N3337 [dcl.init.aggr]/7:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list ([dcl.init.list]). [...]
Copy-initialization isn't even involved here. And N3337 [dcl.init.list]/3.1:
List-initialization of an object or reference of type
T
is defined as follows:
If the initializer list has no elements and
T
is a class type with a default constructor, the object is value-initialized.[...]
No change.