联合本身初始化后如何初始化联合成员?

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;
};

假设 Y 是一个复杂的大型类型,需要进行初始化。你会怎么做?

我问的原因:我有一个这样的联合,其中“Y”将是 std::string。当然,std::string 有一个赋值运算符,所以它的值可以稍后设置。但是我不确定 std::string 是否需要初始化,除了稍后是否会分配给它。也许当 std::string 被分配给它时,它建立在它已经拥有的数据之上:它覆盖了已经存在的数据,因此已经需要一个指向数据的有效指针。

如果联合成员是微不足道的,激活成员的传统方法是分配它:

uk.y = some_value;

如果成员不是平凡的(例如std::string),那么您必须首先销毁当前活动的成员,然后使用 placement-new 初始化另一个成员:

uk.x.~X();
::new (&uk.y) Y /*parenthesised or braced init arguments here*/;

请注意,如果联合包含非平凡成员,则其所有特殊成员函数(默认构造函数除外)从析构函数到复制构造函数再到赋值运算符都将被隐式删除。因此,您需要为类似联合的 class 包装它定义那些。每个人都必须知道当前活跃的成员,并据此采取行动。

另请注意,析构函数不会隐式销毁联合成员,这与正常 class 不同,后者会在析构函数主体之后销毁成员。这必须像我上面显示的那样明确地完成。


P.S。标准库中有一个标准的标记联合:std::variant。无需自己发明。