Skip to content

CWG3041 [class.dtor] trivial union proposal added overly aggressive rule to delete a union destructor #722

Description

@brevzin

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 type M (or possibly multi-dimensional array thereof) where M 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 V S has a default member initializer and M has a destructor that is non-trivial,

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.

Activity

  1. rbhrenkap commented on Jul 18, 2025

    @rbhrenkap

    I am not sure if there should be a difference between U1/2 and U3/4.

    Assuming such a union is used in a non-trivial way, it may have initialized any of its members. How it was initialized initially does not seem so important to me. Therefore, I think the rules for the defaulted destructor for unions should not depend on properties of the union construction, only on whether the destructors of the members are all trivial or not.
    The rationale from the compiler perspective would be "hey you might have activated a member here that needs a non-trivial destructor and I am not sure what will be there, so just to remind you that I will not be cleaning up for you, I will not provide you with a default destructor, sorry".

    So I would expect U -> trivial destructor, U1/2/3/4 -> deleted destructor.

  2. frederick-vs-ja commented on Jul 21, 2025

    @frederick-vs-ja

    So I would expect U -> trivial destructor, U1/2/3/4 -> deleted destructor.

    You seemed to suggest the design of union destructors before P3074R7.

  3. jensmaurer commented on Jul 27, 2025

    @jensmaurer
    Member
  4. changed the title [-][class.dtor] trivial union proposal added overly aggressive rule to delete a union destructor[/-] [+]CWG3041 [class.dtor] trivial union proposal added overly aggressive rule to delete a union destructor[/+] on Jul 27, 2025
  5. rbhrenkap commented on Jul 28, 2025

    @rbhrenkap

    So I would expect U -> trivial destructor, U1/2/3/4 -> deleted destructor.

    You seemed to suggest the design of union destructors before P3074R7.

    Yes. I see this was requested by core. It kind of makes sense if you think about it and in that light the wording changes by Barry are an improvement. I still have some residual mental dissonance because of the mixing on concerns between construction and destruction but I currently have no better proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions