6

In the following program the union U has two fields a and b, each with distinct default value. If one creates a variable of type U using aggregate initialization {} what are the value and the active member of the union?

#include <iostream>

struct A { int x = 1; };
struct B { int x = 0; };

union U {
    A a;
    B b;
};

int main() {
    U u{};
    std::cout << u.a.x;
}

Surprisingly the compilers diverge here: Clang prints 1 and GCC prints 0, demo: https://gcc.godbolt.org/z/8Tj4Y1Pv1

Is there a bug in one of the compilers or the behavior here is not defined by the standard?

Fedor
  • 17,146
  • 13
  • 40
  • 131
  • When you initialize `u` you're doing [*value initialization*](https://en.cppreference.com/w/cpp/language/value_initialization). And since `U` is not a class type, it will lead to `u` being [*zero* initialized](https://en.cppreference.com/w/cpp/language/zero_initialization). – Some programmer dude Aug 22 '21 at 12:47
  • *Surprisingly the compilers diverge here: Clang prints 1 and GCC prints 0* - seems like defect, it should be 0 – Marcin Poloczek Aug 22 '21 at 12:59
  • @Someprogrammerdude `U` is a class type, it is union aggregate class. Note the normative term "_non-union_ aggregate", e.g. used in [dcl.init.aggr]/5. Thus, this is aggregate initialization and [dcl.init.aggr]/5 applies (/5.5 for `U u{};` followed by /5.1 for the first data member `a` of the union). – dfrib Aug 22 '21 at 13:39

1 Answers1

6

Clang is correct, GCC is wrong

As per [dcl.init.aggr]/1:

An aggregate is an array or a class ([class]) with

  • (1.1) no user-declared or inherited constructors ([class.ctor]),
  • (1.2) no private or protected direct non-static data members ([class.access]),
  • (1.3) no virtual functions ([class.virtual]), and
  • (1.4) no virtual, private, or protected base classes ([class.mi]).

A, B and U are all aggregate classes, although the prior to are non-union aggregate classes, which the former does not qualify as.

As per [dcl.init.aggr]/5 [emphasis mine]:

For a non-union aggregate, each element that is not an explicitly initialized element is initialized as follows:

  • (5.1) If the element has a default member initializer ([class.mem]), the element is initialized from that initializer.
  • (5.2) Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
  • (5.3) Otherwise, the program is ill-formed.

If the aggregate is a union and the initializer list is empty, then

  • (5.4) if any variant member has a default member initializer, that member is initialized from its default member initializer;
  • (5.5) otherwise, the first member of the union (if any) is copy-initialized from an empty initializer list.

Thus

U u{};

is aggregate initialization, with the result that the first data member of the union class, namely the data member a of type A (which is a non-union aggregate class), is copy-initialized from an empty initializer list. As the single data member x of the type A has a default member initializer, then as per [dcl.init.aggr]/5.1 above, the data member x is initialized by its default member initializer.

Thus, Clang is correct, and GCC is wrong.


GCC bug report

dfrib
  • 70,367
  • 12
  • 127
  • 192