【问题标题】:C++ "Choice" UnionC++“选择”联盟
【发布时间】:2011-03-19 06:06:38
【问题描述】:

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

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

这就是我现在正在做的事情,(只有两个选择,最多可能是 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();
    }
};

感谢您的帮助

【问题讨论】:

  • 这在 Pascal 中称为变体记录。 C/C++ 对它们没有特殊的语法,可能是为了便于实现,并且因为联合提供了一种实现类似结果的方法,所以何必费心。
  • 感谢您的信息+历史 :)

标签: c++ unions choice


【解决方案1】:

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

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

【讨论】:

  • boost:variant 似乎很接近,我有什么方法可以区分具有相同类型的两个不同选择吗?假设choice2和choice3都是整数,但代表不同的东西,有什么办法用variant来处理?
  • 您可以为此定义自己的通用模板类型包装器,例如:template<int Discriminant, typename Value> struct distinct_type { Value value; }。然后,您可以根据需要创建不同的包装器:distinct_type<0, int>distinct_type<1, int> 等。更好的是,定义一个 enum 并将其常量用于 Discriminant
【解决方案2】:

更一般地说,这是一个“有区别的工会”或tagged union。如前所述, boost::variant 或 boost::any 都是该策略的实现。

【讨论】:

  • 感谢正确的名称...不太确定该叫什么
【解决方案3】:

即使您和我一样,通常更喜欢变体而不是继承(我是 ML 类型的人),但继承是 C++ 的实现方式。

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

【讨论】:

    【解决方案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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-01
      • 2021-12-28
      • 1970-01-01
      • 2011-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-09
      相关资源
      最近更新 更多