Please consider the following example with an aggregate struct B
with the field of type U
. The field's destructor is private, but available to the aggregate due to friend
declaration, and not available for calling from main
function:
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;
}
And if one uses aggregate initialization B{}
then the code is accepted only by GCC, while Clang reports the error:
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() {}
^
Demo: https://gcc.godbolt.org/z/c33Gbqfqh
I have not found any mentions of the "destructor" in https://en.cppreference.com/w/cpp/language/aggregate_initialization . Is it really required by the standard for the aggregate field to have its destructor available to every potential user of the aggregate?
Access to the destructor is indeed required by the standard. Quoting from N4868 (closest to the published C++20), [dcl.init.aggr]/8 says:
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 ]
For completeness, [class.dtor]/15 says:
[...] 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 was added by the resolution of DR2227, first published in C++20. It's a defect report, so compilers should apply it to previous standard versions as well.