C ++“选择”联盟

时间:2010-07-21 21:26:45

标签: c++ unions choice

不确定是否有这个术语,“选择”似乎有效。我在C ++工作,我需要创建一些联合,其中union表示联盟成员之一的选择。跟踪当前的“选择”并始终可用。我目前正在手动编写这些“联合”代码,但我想知道是否有任何巧妙的技巧可以自动执行此类(半)。

我遇到了没有赋值运算符重载或非trival构造函数或复制构造函数的union限制,这是我第一次试图实现这一点,但意识到因为我实际上正在跟踪当前的“选择”,所以在几乎所有情况下都要采取非常明确的行为。

这就是我现在正在做的事情(只有两个选择,最多可以是10或15),并且它的代码数量非常大,几乎所有代码都只是样板。另外,如果有人对我下面的内容是否有效甚至是有效的评论有任何意见,那么仍然会发现一些C ++的疯狂......

struct MyChoice
{
    struct Choice1
    {
        int a;
        char* b;
    };

    struct Choice2
    {
        bool c;
        double d;
    };

    enum Choice
    {
        Choice_Choice1,
        Choice_Choice2
    } choice;

    char _value[max(sizeof(Choice1),sizeof(Choice2))]; // could be private
    Choice1& choice1()
    {
        if(choice == Choice_Choice2)
        {
            (*(Choice2*)_value)->~Choice2();
            (*(Choice1*)_value) = Choice1();
            choice = Choice_Choice1;
        }
        return *(Choice1*)_value;
    }
    Choice2& choice2()
    {
        if(choice == Choice_Choice1)
        {
             (*(Choice1*)_value)->~Choice1();
             (*(Choice2*)_value) = Choice2();
             choice = Choice_Choice2; 
        }
        return *(Choice2*)_value;
    }
    MyChoice()
    {
       _choice = Choice_Choice1;
       (*(Choice1)_value) = Choice1();
    }
    MyChoice(const MyChoice& other)
    {
       this->_choice = other.choice;
       if(this->_choice == Choice_Choice1)
          (*(Choice1*)_value) = other.choice1();
       else
          (*(Choice2*)_value) = other.choice2();
    }
    ~MyChoice()
    {
        if(_choice == Choice_Choice1)
            (*(Choice1)_value)->~Choice1();
        else
            (*(Choice2)_value)->~Choice2();
    }
};

感谢您的帮助SO

4 个答案:

答案 0 :(得分:15)

尝试查看boost :: any和boost :: variant。 第一个允许您在boost :: any变量中插入任何类型,跟踪其类型。 它更像是“运行时检查”类型。 第二个强制您定义要插入的所有类型(即boost :: variant< Choice1,Choice2,...>),但在编译时强制执行更多类型检查。

两者都用于存储不同类型的对象,例如具有异构容器(std :: vector可以处理std :: string或int)。

答案 1 :(得分:6)

更一般地说,这是一个'受歧视的联盟'或tagged union。如前所述,boost :: variant或boost :: any都是这种策略的实现。

答案 2 :(得分:4)

在C ++ 17中,标准库中直接提供了一种std::variant类型。

以下是cppreference中示例源代码的一小部分摘录:

#include <variant>
#include <string>
#include <cassert>

using namespace std::literals;

int main()
{
    std::variant<int, float> v, w;
    v = 12; // v contains int
    int i = std::get<int>(v);
    w = std::get<int>(v);
    w = std::get<0>(v); // same effect as the previous line
    w = v; // same effect as the previous line

//  std::get<double>(v); // error: no double in [int, float]
//  std::get<3>(v);      // error: valid index values are 0 and 1
}

答案 3 :(得分:3)

即使你像我一样,并且通常更喜欢变体继承(我是ML类型的人),继承是C ++的方法。

使用指向boost::variant<Apple, Pear, Banana>对象的智能指针,而不是使用Fruit的对象。继承具有开放的优势 - 您可以随时添加更多类型的Fruit。虚拟方法通常比switch或if语句更清晰。给继承一个机会;你会学会喜欢它。