首页 > 解决方案 > 不可复制的类数据成员的统一初始化导致 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 版本,同样的错误。)

标签: c++c++11gcclanguage-lawyer

解决方案


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.

[dcl.init.list]/3.4:

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.


推荐阅读