Search code examples
c++language-lawyerunionsaggregate-initialization

Aggregate initialization of a union in C++ with `{}`


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?


Solution

  • 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