2010-07-21 2 views
4

Не уверен, что существует термин для этого, кажется, что «выбор» работает. Я работаю на C++, и у меня есть множество союзов, которые мне нужно создать, где союз представляет собой выбор одного из членов профсоюза. Текущий «выбор» отслеживается и всегда доступен. В настоящее время я кодирую эти «союзы» вручную, но мне интересно, есть ли какой-нибудь опрятный трюк для такого рода вещей (полу) автоматически.C++ «Choice» 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

+0

Это называется вариантом записи в Паскале. C/C++ не имеет специального синтаксиса для них, вероятно, для простоты реализации, и потому что профсоюзы обеспечивают способ достижения аналогичного результата, поэтому зачем беспокоиться. – ninjalj

+0

Спасибо за информацию + историю :) – LorenVS

ответ

14

Попробуйте найти boost :: any и boost :: variant. Первый способ позволяет вставить любой тип в boost :: любую переменную, отслеживая ее тип. Это скорее тип «check-at-runtime». Второе - заставлять вас определять все типы, которые нужно вставить (т.е. boost :: variant < Choice1, Choice2, ...>), но прибегнуть к дополнительной проверке типов во время компиляции.

Оба используются для хранения объектов разных типов, например, для гетерогенных пометок (std :: vector может обрабатывать std :: string или int, например).

+0

boost: вариант кажется близким, был ли какой-нибудь способ для меня различать два разных варианта, имеющих один и тот же тип? Предположим, что choice2 и choice3 являются целыми числами, но представляют разные вещи, любым способом справиться с этим вариантом? – LorenVS

+2

Вы можете определить свою собственную универсальную оболочку типа шаблона для этого, например: 'template struct distinct_type {Значение; } '. Затем вы можете создать различные обертки по мере необходимости: 'distinct_type <0, int>', 'distinct_type <1, int>' и т. Д. Еще лучше определите 'enum' и используйте его константы для' Discriminant'. –

6

В целом это «дискриминационный союз» или tagged union. Как упоминалось boost :: variant или boost :: any - оба варианта реализации этой стратегии.

+0

спасибо за правильное имя ... не совсем уверен, как это назвать – LorenVS

3

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

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