Search code examples
c++aggregatelanguage-lawyerprivate-members

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


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?


Solution

  • 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.