C ++ "Выбор" Союз - PullRequest
       3

C ++ "Выбор" Союз

6 голосов
/ 22 июля 2010

Не уверен, что для этого есть термин "выбор", кажется, работает.Я работаю в 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();
    }
};

Спасибо за вашу помощь1008 *

Ответы [ 4 ]

15 голосов
/ 22 июля 2010

Попробуйте посмотреть на boost :: any и boost :: option.Первый позволяет вам вставить любой тип в boost :: any, отслеживая его тип.Это скорее тип проверки во время выполнения.Второй заставляет вас определить все типы для вставки (то есть boost :: variable ), но принудительно выполнять дополнительную проверку типов во время компиляции.хранить объекты разных типов, например, чтобы иметь разнородные контейнеры (например, std :: vector может обрабатывать std :: string или int).

6 голосов
/ 22 июля 2010

В более общем смысле это «дискриминационный союз» или теговый союз . Как уже упоминалось, Boost: Option или Boost :: Any являются реализациями этой стратегии.

4 голосов
/ 28 августа 2018

В 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 голосов
/ 22 июля 2010

Даже если вы похожи на меня и обычно предпочитаете варианты наследования (я парень типа ML), наследование - это способ C ++.

Вместо использования объекта boost::variant<Apple, Pear, Banana>, используйте умный указатель на объект Fruit.Наследование имеет то преимущество, что оно открыто - вы всегда можете добавить больше типов Fruit.Виртуальные методы обычно намного чище, чем переключатели или операторы if.Дать наследство шанс;ты научишься любить это.

...