Description
Reference (section label): [class.dtor]
Link to reflector thread (if any):
Issue description:
P3074R7 in its latest revision tried to add symmetry for the constructor and destructor rules, on the premise that if the constructor constructs something with a non-trivial destructor the defaulted destructor should be deleted. i.e. if { U u; }
works it should be sensible.
However, the wording right now says:
A defaulted destructor for a class X is defined as deleted if
- [...],
- X is a union and
- overload resolution to select a constructor to default-initialize an object of type X either fails or selects a constructor that is either deleted or not trivial, or
- X has a variant member V of class type M (or possibly multi-dimensional array thereof) where V has a default member initializer and M has a destructor that is non-trivial,
- or, [...]
That means that, among other things, this type:
union U {
U(int i) : i(i) { }
int i;
};
now has a deleted destructor.
We should only delete the defaulted destructor for a union if:
- there is a subobject
S
of class typeM
(or possibly multi-dimensional array thereof) whereM
has a destructor that is deleted, inaccessible from the defaulted destructor, or non-trivial, AND EITHER- overload resolution to select a constructor to default-initialize an object of type X either fails or selects a constructor that is either deleted or user-provided
not trivial, OR X has a variant member V of class type M (or possibly multi-dimensional array thereof) where VS has a default member initializerand M has a destructor that is non-trivial,
- overload resolution to select a constructor to default-initialize an object of type X either fails or selects a constructor that is either deleted or user-provided
The user-provided addition is because we don't want to reject either of these:
union U1 { string s; U1* next = nullptr; };
union U2 { U2() = default; string s; U2* next = nullptr; };
Those default constructors aren't trivial, but this case is okay. It's only these cases that we want to reject:
union U3 { U3(); string s; U3* next; };
union U4 { string s = "hi"; U4* next; };
because we don't know what U3
might initialize — it might initialize s
, so we preemptively delete the destructor in that case. And U4
we know we're initializing s
, so we definitely want to reject.