Playing with constexpr
and union
I found out, that I can't change active member of an union
in constexpr
. Just one exception: union
of empty classes.
constexpr bool t()
{
struct A {};
struct B {};
union U { A a; B b; } u{};
u.a = A{};
u.b = B{};
return true;
}
static_assert(t());
constexpr bool f()
{
struct A { char c; };
struct B { char c; };
union U { A a; B b; } u{};
u.a = A{};
u.b = B{}; // error originating from here
return true;
}
static_assert(f());
First function may produce constant expression. But the second can't. Hard error says:
main.cpp:23:15: error: static_assert expression is not an integral constant expression
static_assert(f());
^~~
main.cpp:20:11: note: assignment to member 'b' of union with active member 'a' is not allowed in a constant expression
u.b = B{};
^
main.cpp:20:9: note: in call to '&u.b->operator=(B{})'
u.b = B{};
^
main.cpp:23:15: note: in call to 'f()'
static_assert(f());
^
1 error generated.
1.) Is it possible to change active member of union
in constant expressions?
I tried to destruct active member, but it is not allowed, due to destructors are not constexpr
in general. Also I tried to use placement operator new
(::new (&u.b) B{2};
), but also unusccessfull. reinterpret_cast
also not allowed in constant expressions. Altering members of common initial subsequence prohibited too.
2.) Are there a ways to make mutable (in sense of changing active alternative type) literal boost::variant
-like type? How looks like its storage if it possible?
3.) Is it undefined behaviour to make assignment to non-active members of union
of trivially copy-assignable types at runtime? Is it undefined behaviour to construct non-active member of union
of trivially-copyable types using placement operator new
avoiding preliminary destruction of active member at runtime?
ADDITIONAL:
I can change entire literal type union
, but not its non-active member:
constexpr
bool
f()
{
struct A { char c; };
struct B { char c; };
union U
{
A a; B b;
constexpr U(A _a) : a(_a) { ; }
constexpr U(B _b) : b(_b) { ; }
};
U a(A{});
a.a = A{}; // check active member is A
U b(B{});
b.b = B{}; // check active member is B
a = b;
a = B{}; // active member is B!
return true;
}
static_assert(f());
Therefore for literal type variant
of trivially copyable types conversion assignment operator would be template< typename T > constexpr variant & operator = (T && x) { return *this = variant(std::forward< T >(x)); }
.