What makes a union member active?
I've read chapter 9.5 of the C++14 standard (the one about unions), but I haven't found a clear answer to what makes a union member active.
There is a note:
In general, one must use explicit destructor calls and placement new operators to change the active member of a union.
So for example,
union U {
int i;
short s;
} u;
new(&u.i) int(42);
Okay, placement new changes the active member, it's clear. But we usually don't use placement new when working with types with trivial constructors.
Does operator=
change the active member without UB?
u.i = 42;
Here, operator=
called on an unconstructed object. Is it well defined?
What about this?
struct A {
int i0;
int i1;
};
union U {
A a;
short s;
} u;
What makes a
to be the active member of u
? Is setting both i0
& i1
enough?
u.a.i0 = 42;
u.a.i1 = 99;
What if I write:
u.a.i0 = 42; // supposedly this doesn't change the active member to a, as i1 isn't set
int x = u.a.i0; // is it fine to read from a.i0? a is not an active member supposedly
After u.a.i0 = 42;
, the active member isn't changed to a
(I think), so is it UB to do int x = u.a.i0;
?
Does C++17 improve on the description of active members?
In C++17, a paragraph was added that explicitly discusses cases like u.i = 42
:
[class.union]/5 When the left operand of an assignment operator involves a member access expression (8.2.5) that nominates a union member, it may begin the lifetime of that union member, as described below. For an expression
E
, define the setS(E)
of subexpressions ofE
as follows:(5.1) — If
E
is of the formA.B
,S(E)
contains the elements ofS(A)
, and also containsA.B
ifB
names a union member of a non-class, non-array type, or of a class type with a trivial default constructor that is not deleted, or an array of such types.(5.2) — If
E
is of the formA[B]
and is interpreted as a built-in array subscripting operator,S(E)
isS(A)
ifA
is of array type,S(B)
ifB
is of array type, and empty otherwise.(5.3) — Otherwise,
S(E)
is empty.In an assignment expression of the form
E1 = E2
that uses either the built-in assignment operator (8.18) or a trivial assignment operator (15.8), for each elementX
ofS(E1)
, if modification ofX
would have undefined behavior under 6.8, an object of the type ofX
is implicitly created in the nominated storage; no initialization is performed and the beginning of its lifetime is sequenced after the value computation of the left and right operands and before the assignment. [ Note: This ends the lifetime of the previously-active member of the union, if any (6.8). —end note ]
(Followed by a longish example that I'm too lazy to format properly, but which you can see here.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With