不可复制类数据成员的统一初始化导致gcc错误

Uniform initialization of non-copiable class data member cause to gcc error

本文关键字:初始化 gcc 错误 可复制 数据成员      更新时间:2023-10-16

假设我们有这样的代码:

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() { }时,GCC返回错误use of deleted function 'A::A(const A&)'。Clang和MSVC仍在编译。

当我编写A的复制构造函数时,GCC会编译,但在运行时从未调用过该构造函数。GCC需要复制构造函数做什么?是GCC错误吗?(我在GodPolt.org上试过所有GCC版本,都有同样的错误。(

这是一个GCC错误。

B的默认构造函数使用聚合初始化来初始化a,而不使用初始化器。[dcl.init.agr]/8:

如果列表中的初始值设定项子句少于非并集聚合中的元素,则每个元素都不是显式的初始化如下:

  • 如果元素有默认的成员初始值设定项([class.mem](,则从该初始值设定值初始化元素。

  • 否则,如果该元素不是引用,则从空的初始值设定项列表([dcl.init.list](复制初始化该元素

  • 否则,程序就是格式错误的。

[…]

因此a[0]是从{}复制初始化的,这是复制列表初始化。这可能是GCC开始感到困惑的地方—复制初始化不一定涉及复制构造函数。

[dcl.init.list]/3.4:

T类型的对象或引用的列表初始化定义为如下:

  • […]

  • 否则,如果初始值设定项列表没有元素,并且T是具有默认构造函数的类类型,则对象为值已初始化。

  • […]

因此,直接使用A的默认构造函数。不涉及复制构造函数。也不是琐碎。


如果您担心C++11和C++17之间的差异,N3337[dcl.init.agr]/7:

如果列表中的初始值设定项子句少于聚合中的成员,然后每个成员未显式初始化应该从一个空的初始化器列表([dcl.init.list](初始化。[…]

此处甚至不涉及复制初始化。和N3337[dcl.init.list]/3.1:

定义了类型为T的对象或引用的列表初始化如下所示:

  • 如果初始值设定项列表没有元素,并且T是具有默认构造函数的类类型,则对象被值初始化。

  • […]

没有变化。