Reputation: 121
If it is even possible, how do you initialize a union member after the union itself was initialized?
struct U_Keeper
{
U_Keeper() : x();
bool HoldingTypeU {true};
union
{
X x;
Y y;
};
};
void doSomething(){
U_Keeper uk;
doSomethingWithX(uk.x);
// set uk to Y mode and initialize uk.y
uk.HoldingTypeU = false;
};
Let's say Y is a complicated, big type that needs to be initialized. How would you go about doing that?
The reason I'm asking: I have a union like this where "Y" would be an std::string
. Of course, an std::string
has an assignment operator, so its values can be set later on. But I'm not sure if an std::string
needs to be initialized necessarily, aside from whether it will be assigned to later on or not. Maybe when an std::string
is assigned to it builds upon the data it already had: it overrides the data that was already there, thus already needing a valid pointer to the data.
Upvotes: 0
Views: 411
Reputation: 238311
If the union members are trivial, the traditional way to activate a member is to assign it:
uk.y = some_value;
If the members are non-trivial (such as std::string
), then you must instead first destroy the currently active member, and then initialise the other using placement-new:
uk.x.~X();
::new (&uk.y) Y /*parenthesised or braced init arguments here*/;
Note that if the union contains non-trivial members, then all of its special member functions - except default constructor - from destructor to copy constructor to assignment operators will be be implicitly deleted. So, you'll need to define those for the union-like class wrapping it. Each of those must know the currently active member, and act accordingly.
Note also that the destructor doesn't implicitly destroy the union member unlike normal class which would destroy members after destructor body. That must be done explicitly like I showed above.
P.S. There is a standard tagged union in the standard library: std::variant
. No need to invent your own.
Upvotes: 2