Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 895
Fedor Avatar asked Aug 22 '21 12:08

Fedor


People also ask

Can we initialize unions in C?

C allows you to initialize a union in two ways: Initialize a union by initializing the first member of a union. Or initialize a union by assigning it to another union with the same type.

How do you initialize a union?

An initializer for a structure is a brace-enclosed comma-separated list of values, and for a union, a brace-enclosed single value. The initializer is preceded by an equal sign ( = ).

How do you declare and initialize a union member?

Initializing Union Variable union data { int var1; double var2; char var3; }; union data j = {10}; This statement initializes the union variable j or in other words, it initializes only the first member of the union variable j .

How many union members can be initialized?

A union can be initialized on its declaration. Because only one member can be used at a time, only one can be initialized. To avoid confusion, only the first member of the union can be initialized.


1 Answers

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

  • Bug 102013 - Incorrect aggregate initialization of union
like image 115
dfrib Avatar answered Sep 21 '22 17:09

dfrib