I would like to pick the union member initialized in the constructor based on an argument. The following is an example that works:
struct A {
union {
int i;
float f;
};
A(double d, bool isint) {
if (isint) new(&i) int(d);
else new(&f) float(d);
}
};
While I'm using int
and float
, the goal is to work with other more complex types (but still allowable in a C++14 union), hence the use of placement-new (and not an assignment).
The problem is that this constructor cannot be constexpr
as placement-new is not allowed in constexpr
methods. Is there any way around this (other than making the isint
argument part of the formal type system)? Some type of conditional initalizer list would work, but I'm unaware of a way to do it.
A constexpr function must accept and return only literal types. A constexpr function can be recursive. It can't be virtual. A constructor can't be defined as constexpr when the enclosing class has any virtual base classes.
The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time constant expressions are allowed (provided that appropriate function arguments are given).
A union can have member functions (including constructors and destructors), but not virtual functions. A union cannot have base classes and cannot be used as a base class.
The principal difference between const and constexpr is the time when their initialization values are known (evaluated). While the values of const variables can be evaluated at both compile time and runtime, constexpr are always evaluated at compile time.
There is a trick. The key pieces are:
Putting this together, we get:
template<typename T> struct tag {};
struct A {
union {
int i;
float f;
};
constexpr A(tag<int>, double d) : i(d) {}
constexpr A(tag<float>, double d) : f(d) {}
constexpr A(double d, bool isint) : A(isint ? A(tag<int>(), d) : A(tag<float>(), d)) {}
};
constexpr A a(1.0, true); // ok, initializes 'i'
constexpr A b(5, false); // ok, initializes 'f'
This is accepted by recent Clang, GCC, and EDG, and requires only C++11 constexpr
.
Warning: GCC 5.1.0 had a bug where it miscompiled the above code (initializing a
and b
to 0
); this bug is not present in earlier or later versions of GCC.
For trivially constructible objects, there is no need for new
. You can begin the object lifetime, and select the active union
member, simply by the assignment operator.
struct A {
union {
int i;
float f;
};
A(double d, bool isint) {
if (isint) i = d;
else f = d;
}
};
If there's really a constructor somewhere inside the member, then it's necessary to use Richard's answer.
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