聚合字段析构函数必须可用于在 C++ 中创建聚合的代码吗?

Must aggregate field destructor be available for the code creating the aggregate in C++?

请考虑以下示例,其中聚合 struct B 的字段类型为 U。该字段的析构函数是私有的,但由于 friend 声明而可用于聚合,并且不可用于从 main 函数调用:

class U { 
    ~U() {}
    friend struct B;
};

struct B { U v{}; };

int main()
{
    B b; //ok everywhere
    auto pb = new B; //ok everywhere
    delete pb;
    pb = new B{}; //ok in GCC, error in Clang
    delete pb;
}

并且如果使用聚合初始化 B{} 那么代码只被 GCC 接受,而 Clang 报告错误:

error: temporary of type 'U' has private destructor
    pb = new B{}; //ok in GCC, error in Clang
               ^
<source>:2:5: note: implicitly declared private here
    ~U() {}
    ^

演示:https://gcc.godbolt.org/z/c33Gbqfqh

我在 https://en.cppreference.com/w/cpp/language/aggregate_initialization 中没有发现任何关于“析构函数”的提及。 聚合字段的标准是否真的要求它的析构函数可供聚合的每个潜在用户使用?

标准确实要求访问析构函数。引用 N4868(最接近已发布的 C++20),[dcl.init.aggr]/8 说:

The destructor for each element of class type is potentially invoked from the context where the aggregate initialization occurs. [ Note: This provision ensures that destructors can be called for fully-constructed subobjects in case an exception is thrown. — end note ]

为了完整性,[class.dtor]/15 说:

[...] A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.


[dcl.init.aggr]/8 由 DR2227 决议添加,首次发布于 C++20。这是一个缺陷报告,因此编译器也应该将其应用于以前的标准版本。