如何创建此已标记联合的实例?关于已删除构造函数的编译器错误

How do I create an instance of this tagged union? Compiler errors about deleted constructor

本文关键字:实例 错误 编译器 构造函数 删除 何创建 创建      更新时间:2023-10-16

这是我标记的联合:

struct UniformVariant
{
    enum class UNIFORM_TYPE {FLOAT, INT32, VEC2, VEC3, VEC4, MAT4} type;
    union
    {
        float f;
        int i;
        glm::vec2 v2;
        glm::vec3 v3;
        glm::vec4 v4;
        glm::mat4 m4;
    } value;
};

如果我试着这样使用它:

void some_function()
{
    UniformVariant v;
    some_other_function(v);
}

我收到一个编译错误use of deleted function 'UniformVariant::UniformVariant()'

它进一步表示,它被隐含地删除了,因为默认定义格式不正确。所以我尝试在UniformVariant 中添加一个构造函数

UniformVariant() : value(0.0f), type(UNIFORM_TYPE::FLOAT) { };

但是类似的问题。我认为这与工会中包含阶级类型有关;但我想不出正确使用它的语法。

来自[class.union]中的注释:

如果有任何非静态数据成员的具有非平凡的默认构造函数(12.1)、复制构造函数(12.8)、移动构造函数(12.8),复制赋值运算符(12.8)、移动赋值运算符(1280)或析构函数(12.4)联合的成员函数必须由用户提供,否则它将被隐式删除(8.4.3)。

该并集中的一个类型有一个非平凡的默认构造函数,因此不能默认构造该并集。考虑一下这个更简单的复制:

struct X {
    X() { }  
};
struct Y {
    union {
        float f;
        X x;
    } value;
};
int main()
{
    Y y;
}

X有一个非平凡的默认构造函数,因此Y::Y()被隐式删除,因为匿名联合默认构造函数被隐式地删除。

然而,您可以为该联合提供自己的默认构造函数:

union U {
    U() : f(0.f) { }
    float f;
    X x;
} value;

现在该示例编译完毕。

然而,如果你只是在实现自己的标记联合,我强烈建议你使用Boost.Variant。它非常有用,正好解决了这个问题。

当前联合的问题是它没有指定哪个成员最初应该是活动的。

C++11确实有一个名为匿名联合的功能,在该功能中,最初没有成员处于活动状态,您可以根据需要激活每个成员。如果列表中没有具有非平凡构造函数的类型,则此功能对于实现您正在尝试的类似Variant的对象至关重要。

但是,您的联合不是匿名的,因为它有一个命名实例value

如果非匿名联合包含任何具有非平凡构造函数的成员(例如glm类),则该联合必须具有一个构造函数,该构造函数仅初始化其中一个成员。(即使提供大括号或相等的初始值设定项也不够)。


除了巴里的好建议之外,你的另一个选择是改用匿名联盟:

union
{
    float f;
    int i;
    glm::vec2 v2;
    glm::vec3 v3;
    glm::vec4 v4;
    glm::mat4 m4;
};
// ^^^^^ no name

当这种情况发生时,fiv2等将被"提升"为UniformVariant级别的名称。你现在可以写:

UniformVariant v;
v.i = 1;

再次解释初始化:当结构包含匿名并集时,它实际上不会初始化任何成员。在使用成员时,必须手动构造和销毁它。

对于像int这样的基元类型,您可以通过分配给它们来激活它们,而不需要去激活。但对于类类型,您必须使用placement-new和析构函数调用,例如:

new(&v2) glm::vec2(...args...);

当你处理完该成员时:

v2.~vec2();

为了避免错误,您可能希望使并集位于private部分,并为X成员提供用于设置活动成员的函数。

相关文章: