联合的最多一个非静态数据成员可以有一个大括号或等号初始化器。 [注意:如果联合的任何非静态数据成员具有非平凡的默认构造函数 (12.1)、复制构造函数 (12.8)、移动构造函数 (12.8)、复制赋值运算符 (12.8)、移动赋值运算符 (12.8),或析构函数(12.4),联合的相应成员函数必须是用户提供的,否则它将为联合隐式删除(8.4.3)。 ——尾注]
相关部分是
如果联合的any非静态数据成员具有非平凡的[成员函数] [...],则[则]联合的相应成员函数必须是用户提供,否则将被隐式删除。
因此您将不得不手动实现构造函数和析构函数(如果您还需要它们,还需要复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符)。
编译器无法提供默认构造函数,因为它无法知道默认构造函数应该初始化哪个联合成员,或者析构函数需要销毁哪个联合成员。
因此使用默认构造函数或尝试=default; 将不起作用,您必须提供用户指定的实现。
基本实现可能如下所示:
struct Foo
{
bool isVector;
union
{
std::vector<float> vec;
struct { float width, height; };
};
// by default we construct a vector in the union
Foo() : vec(), isVector(true) {
}
void setStruct(float width, float height) {
if(isVector) {
vec.~vector();
isVector = false;
}
this->width = width;
this->height = height;
}
void setVector(std::vector<float> vec) {
if(!isVector) {
new (&this->vec) std::vector<float>();
isVector = true;
}
this->vec = vec;
}
~Foo() {
if(isVector) vec.~vector();
}
};
godbolt example
请注意,您需要手动管理联合成员的生命周期,因为编译器不会为您执行此操作。
如果活动的联合成员是struct,但你想激活vector成员,你需要使用placement new在联合中构造一个新的向量。
反之亦然:如果要将活动联合成员从vector切换到struct,则需要先调用vector析构函数。
我们不需要清理struct,因为它很简单,所以不需要调用它的构造函数/析构函数。
如果可能,我建议使用std::variant 或boost::variant,因为它们正是这样做的:它们跟踪活动的联合成员并根据需要调用所需的构造函数/析构函数。