This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115e. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-11-11


2743. Copying non-trivial objects nested within a union

Section: 11.4.5.3  [class.copy.ctor]     Status: open     Submitter: Jiang An     Date: 2023-06-05

Consider:

  #include<new>

  struct A {
    ~A();
  };

  union U {
    alignas(A) unsigned char buf[sizeof(A)];
  };

  void f() {
    U u;
    ::new (u.buf) A();  // object of type A is nested within u because u provides storage
    U v = u;            // #1
  }

Subclause 11.4.5.3 [class.copy.ctor] paragraph 15 specifies:

The implicitly-defined copy/move constructor for a union X copies the object representation (6.8.1 [basic.types.general]) of X. For each object nested within (6.7.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is identified (if the object is a subobject) or created (otherwise), and the lifetime of o begins before the copy is performed.

Thus, the rule seems to require that #1 creates an object of type A inside v.buf (without invoking a constructor of A), and then a copy of the object representation is performed. Creating an object out of thin air can plausibly work only for implicit-lifetime types (11.2 [class.prop] paragraph 9), and meaningfully copying the object representation works only for trivially copyable types (6.8.1 [basic.types.general] paragraph 3). The rule about nested objects should be suitably limited.

Subclause 11.4.6 [class.copy.assign] paragraph 13 has a similar rule for copy/move assignment:

The implicitly-defined copy/move assignment operator for a union X copies the object representation (6.8.1 [basic.types.general]) of X. If the source and destination of the assignment are not the same object, then for each object nested within (6.7.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is created, and the lifetime of o begins before the copy is performed.

Possible resolution:

  1. Change in 11.4.5.3 [class.copy.ctor] paragraph 15 as follows:

    The implicitly-defined copy/move constructor for a union X copies the object representation (6.8.1 [basic.types.general]) of X. For each object of implicit-lifetime type (6.8.1 [basic.types.general]) nested within (6.7.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is identified (if the object is a subobject) or created (otherwise), and the lifetime of o begins before the copy is performed.
  2. Change in 11.4.6 [class.copy.assign] paragraph 13 as follows:

    The implicitly-defined copy/move assignment operator for a union X copies the object representation (6.8.1 [basic.types.general]) of X. If the source and destination of the assignment are not the same object, then for each object of implicit-lifetime type (6.8.1 [basic.types.general]) nested within (6.7.2 [intro.object]) the object that is the source of the copy, a corresponding object o nested within the destination is created, and the lifetime of o begins before the copy is performed.