Member initializers in anonymous unions
11.9.3 [class.base.init]
Daveed Vandevoorde

Created on 2013-02-12.00:00:00 last changed 39 months ago


Date: 2019-10-15.00:00:00

Proposed resolution (October, 2019):

  1. Change 11.4.3 [class.mfct.non.static] paragraph 1 as follows:

  2. ...A non-static member function may also be called directly using the function call syntax ( [expr.call], [over.match.call]) from within the body of a member function of its class or of a class derived from its class, or a member thereof, as described below. .
  3. Change 11.4.3 [class.mfct.non.static] paragraph 3 as follows:

  4. When an id-expression (7.5.4 [expr.prim.id]) that is not part of a class member access syntax ( [expr.ref]) and not used to form a pointer to member ( [expr.unary.op]) is used in a member of class X in a context where this can be used (7.5.2 [expr.prim.this]), if name lookup (6.5 [basic.lookup]) resolves the name in the id-expression to a non-static non-type member of some class C, and if either the id-expression is potentially evaluated or C is X or a base class of X, the id-expression is transformed into a class member access expression ( [expr.ref]) using (*this) (_N4868_. [class.this]) as the postfix-expression to the left of the . operator. [Note: If C is not X or a base class of X, the class member access expression is ill-formed. —end note] Similarly during name lookup, when an unqualified-id ( [expr.prim.id.unqual]) used in the definition of a member function for class X resolves to a static member, an enumerator or a nested type of class X or of a base class of X, the unqualified-id is transformed into a qualified-id ( [expr.prim.id.qual]) in which the nested-name-specifier names the class of the member function. These transformations do This transformation does not apply in the template definition context ( [temp.dep.type]). [Example:...
  5. Delete 11.4.9 [class.static] paragraph 3:

  6. If an unqualified-id ( [expr.prim.id.unqual]) is used in the definition of a static member following the member's declarator-id, and name lookup (6.5.3 [basic.lookup.unqual]) finds that the unqualified-id refers to a static member, enumerator, or nested type of the member's class (or of a base class of the member's class), the unqualified-id is transformed into a qualified-id expression in which the nested-name-specifier names the class scope from which the member is referenced. [Note: See 7.5.4 [expr.prim.id] for restrictions on the use of non-static data members and non-static member functions. —end note]
  7. Change 11.5.2 [class.union.anon] paragraph 1 as follows:

  8. A union of the form

      union { member-specification } ;

    is called an anonymous union; it defines an unnamed type and an unnamed object of that type called an anonymous union object. Each member-declaration in the member-specification of an anonymous union shall either define a non-static data member or be a static_assert-declaration. [Note: Nested types, anonymous unions, and functions cannot shall not be declared within an anonymous union. end note] The names of the members...

Date: 2019-11-15.00:00:00

[Adopted as a DR at the November, 2019 meeting.]

The effect of a non-static data member initializer in an anonymous union is not clearly described in the current wording. Consider the following example:

  struct A {
    struct B {
      union {
        int x = 37;
      union {
        int y = x + 47;  // Well-formed?
    } a;

Does an anonymous union have a constructor that applies a non-static data member initializer? Or is the initialization performed by the constructor of the class in which the anonymous union appears? In particular, is the reference to x in the initializer for y well-formed or not? If the initialization of y is performed by B's constructor, there is no problem because B::x is a member of the object being initialized. If an anonymous union has its own constructor, B::x is just a member of the containing class and is a reference to a non-static data member without an object, which is ill-formed. Implementations currently appear to take the latter interpretation and report an error for that initializer.

As a further example, consider:

  union {       // #1
    union {     // #2
      union {   // #3
        int y = 32;
  } a { } ;

One interpretation might be that union #3 has a non-trivial default constructor because of the initializer of y, which would give union #2 a deleted default constructor, which would make the example ill-formed.

As yet another example, consider:

  union {
    union {
      int x;
    union {
      int y = 3;
    union {
      int z;
  } a { };

Assuming the current proposed resolution of issue 1502, what is the correct interpretation of this code? Is it well-formed, and if so, what initialization is performed?

Finally, consider

  struct S {
    union { int x = 1; };
    union { int y = 2; };
  } s{};

Does this violate the prohibition of aggregates containing member initializers in 9.4.2 [dcl.init.aggr] paragraph 1?

See also issues 1460, 1562, 1587, and 1623.

Date User Action Args
2020-12-15 00:00:00adminsetmessages: + msg6465
2020-12-15 00:00:00adminsetstatus: drafting -> c++20
2013-05-03 00:00:00adminsetstatus: open -> drafting
2013-02-12 00:00:00admincreate