Search code examples
c++default-value

Why does default initialization of a class field in C++ require destructor invocation?


Please help me with this program:

struct U { 
    U(int *) noexcept;
private:
    ~U() noexcept;
};

struct B {
    B();
    ~B();
    U v; //ok
    U w{nullptr}; //ok
    U u = nullptr; //error
};

Here struct U has a private destructor only for demonstration that the destructor is not really invoked by the compiler and to simplify the length of the code.

And struct B has only declared a default constructor and destructor, so the compiler will not generate them in this translation unit.

Also struct B has 3 fields: v, w and u. There is no problem with the v and w fields, but for field u the compiler issues an error about an inaccessible destructor for U:

error: 'U::~U()' is private within this context
   13 |     U u = nullptr; //error

Demo: https://gcc.godbolt.org/z/YooGe9xq6

The questions are:

  1. If B::B() is not compiled in this translation unit, why is field default initialization considered at all?
  2. Is the error because a temporary object is created for the initialization of the u field? (No mandatory copy elision?)
  3. What is the difference between the u and w cases then?

Solution

  • If B::B() is not compiled in this translation unit, why field default initialization is considered at all?

    Because member initialization is part of the class' definition. So it's the class definition itself that's invalid, without having to involve the definition of its constructor(s). In the standard, you can start from [class.mem.general] and follow through brace-or-equal-initializer, which eventually requires a valid "bog-standard" assignment-expression.

    Is the error because of a temporary object created for the initialization of u field? (No mandatory copy elision?)

    Yes, the compiler error in your godbolt link is clear about that:

    <source>:13:11: error: temporary of type 'U' has private destructor
        U u = nullptr; //error
    

    see Why is public destructor necessary for mandatory RVO in C++? for an explanation on why mandatory copy-elision doesn't apply (Thanks @NathanPierson!)

    What is the difference between u and w cases then?

    w is initialized directly without creating a temporary beforehand so creating it works fine because nothing in the expression ever tries to invoke the destructor of U. Obviously any definition of B::~B() would still fail on w, but that's beyond the point.