Single member union creates all sorts of problem when trying to be default constructible and be constexpr compatible.
Consider this code:
struct nontrivial {
constexpr nontrivial(int o) : u{o} {}
int u;
};
union storage {
nontrivial nt;
};
struct optional {
storage s;
};
constexpr auto run() -> int {
optional o;
return o.s.nt.u;
}
int main() {
constexpr int t = run();
}
This is ill formed because optional
has a deleted constructor.
Then a simple fix would be to add a constructor that initialize no union member:
union storage {
constexpr storage() {} // standard says no
nontrivial nt;
};
But it won't work. Constexpr unions must have at least one active member. It cannot be an empty union. To workaround this limitation, a dummy member is added. This makes std::optional
useable in constexpr context.
(Thanks @Barry!) From [dcl.constexpr]/4 (emphasis mine):
The definition of a constexpr constructor whose function-body is not = delete shall additionally satisfy the following requirements:
if the class is a union having variant members ([class.union]), exactly one of them shall be initialized;
if the class is a union-like class, but is not a union, for each of its anonymous union members having variant members, exactly one of them shall be initialized;
for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
for a delegating constructor, the target constructor shall be a constexpr constructor.