【问题标题】:C++ - union with nontrivial class member type?C++ - 与非平凡类成员类型的联合?
【发布时间】:2020-09-24 09:58:09
【问题描述】:

我正在使用一个联合,它的成员是使用菱形继承的类,但程序在分配给该成员时遇到分段错误。

我的怀疑是我需要添加一些复制构造函数,但经过多次尝试后,我仍然无法正确执行此操作。

我在这里包含了一个最小的、可重现的示例。

struct Base
{
    Base() : a(0) {}
    Base(int x) : a(x) {}

    int a;
};

struct Derived1 : virtual public Base
{
    Derived1() {}
};

struct Derived2 : virtual public Base
{
    Derived2() {}
};

struct Final : public Derived1, public Derived2
{
    Final() {}
};

union Example
{
    Final value;
    int i;
};

int main()
{
    Example example{ Final() };
    example.i = -1;

    /* Segfault on the line below. 
     * If the above line

       example.i = -1;
     
     * is removed, the segfault is not encountered. */

    example.value = Final();
}

感谢任何知道如何做到这一点的人。

【问题讨论】:

  • 这就是枚举的工作方式。 example.i = -1;int i; 标记为活动成员,访问其他成员是未定义的行为。不要尝试使用枚举进行类型双关语,它在 C++ 中不起作用,您得到的最好的结果是所谓的“通用初始序列”,但这当然不适用于虚拟。
  • @Quimby 你的意思是联合,而不是枚举,对吧?
  • C++ 联合使用起来真的很烦人,改用std::variant
  • @Quimby “仅在 C 中明确允许通过联合进行类型双关,而不是在 C++ 中” - 是的,但我没有看到 OP 试图在这里进行类型双关。 OP 不会写入一个成员,然后尝试从另一个成员读取。
  • @TedLyngmo 哇,显然我今天不会读也不会写,很抱歉。在这种情况下,请忽略我在此评论部分所说的一切:D

标签: c++ inheritance unions diamond-problem


【解决方案1】:

由于Base类是virtual,很可能有一些内部控制结构,当你分配时它们会被破坏

example.i = -1;

当你重新创建 value 时,例如

new(&example.value) Final();
example.value = Final();

在分配之前,分段错误消失了。虽然,我不会推荐这个 hack。


正如 cmets 中已经提到的,如果您有可用的 C++17,std::variant 会更合适。那么这个例子就变成了

std::variant<Final, int> example{ Final() };
example = -1;
example = Final();

【讨论】:

  • 这不是“黑客”,这是使用工会的合法方式。后面的分配是没有意义的。
【解决方案2】:

从 c++11 开始,与定义自己的构造函数的成员的联合 和/或复制控制成员是允许的,但是当联合有内置类型的成员时,我们可以使用普通赋值来 更改工会持有的价值,但不适用于拥有以下成员的工会 非平凡的类类型。当我们在一个成员之间切换联合的值时 类类型,我们必须构造或销毁该成员。例如看下面的代码

#include <iostream>
#include <new> // for placement new

class Type   // non built in type with constructors
{
private:
    int val;

public:
    Type() : val{0} {    }

    explicit Type(int v) : val{v}  {  }

    Type(const Type &obj) : val{obj.val} {  }

    int getval()  {   return val;  }
};

class unionexample   // I enclose union with class for readability
{
private:
    enum { INT,TYPE } OBJ;

    union
    {
        Type Tval;  // non build in type
        int ival;   // build in type
    };

public:
    unionexample() : ival{0}, OBJ{INT} // default with int
    { }

    unionexample(const unionexample &obj) : OBJ{obj.OBJ}
    {
        switch (obj.OBJ)
        {
        case INT:
            this->ival = obj.ival;
            break;
        case TYPE:
            new (&this->Tval) Type(obj.Tval);
            break;
        }
    }

    unionexample &operator=(int v)
    {
        if (OBJ == TYPE)
        {
            Tval.~Type(); // if it is TYPE destruct it
        }

        ival = v; // assign

        OBJ = INT;

        return *this;
    }

    unionexample &operator=(Type v)
    {
        if (OBJ == TYPE)
        {
            Tval = v;  // if it is alderdy Type just assign
        }

        new (&Tval) Type(v); // else construct

        OBJ = TYPE;

        return *this;
    }

    void print()
    {
        switch (OBJ)
        {
        case INT:
            std::cout << "ival = " << ival << std::endl;
            break;

        case TYPE:
            std::cout << "Tval = " << Tval.getval() << std::endl;
            break;
        }
    }

    ~unionexample()
    {
        if (OBJ == TYPE)  // if it is TYPE we must destruct it
            Tval.~Type();
    }
};

int main()
{
    unionexample ue;
    ue.print();
    ue = Type(1);
    ue.print();
}

输出:

ival = 0
Tval = 1

Run Here

你的情况

int main()
{
    Example example{ value : Final() };     // construct with Final

    example.value.~Final();                 // destruct Final

    example.i = -1;                         // assign int built in type
    
    new(&example.value)  Final();           // construct
    
    example.value.~Final();                 // destruct finally
}

如果您问这个问题不是出于学习目的,您可以使用std::variant 作为其他答案。

谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-28
    • 1970-01-01
    • 2015-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-18
    相关资源
    最近更新 更多