2013-07-18 2 views
6

Мне нужно создать объединение, но 2 члена союза будут иметь один и тот же тип, поэтому мне нужен способ их идентифицировать. Например, в OCaml:Tagged union (aka variant) в C++ с одним и тем же типом несколько раз

type A = 
    | B of int 
    | C of float 
    | D of float 

Boost.Variant, кажется, не поддерживает этот случай, есть известная библиотека, которая поддерживает это?

+3

Почему два одинаковых типа? Только один член союза может использоваться в любой момент времени. – hmjd

+2

Я знаю, но есть случаи, когда вы хотели бы относиться к члену, даже если они имеют один и тот же базовый тип. Небольшим примером может быть тип Expr с 2 членами IntConst из int и IntMutable из int. – maattdd

+2

Но должен быть какой-то другой флаг где-нибудь (содержащий 'struct' или' class'), который указывает, какой член объединения _active_? Это можно использовать для обеспечения дополнительного требуемого значения. – hmjd

ответ

5

Если вы хотите сделать это, я думаю, что лучшим вариантом является, чтобы обернуть те же, но-разные типы-в структуры, которые затем позволяет вариант подталкивание посетить правильный один:

struct Speed 
{ 
    float val_; 
}; 

struct Darkness 
{ 
    float val_; 
}; 

Вы возможно, сможет использовать BOOST_STRONG_TYPEDEF, чтобы сделать это автоматически, но я не уверен, что он будет генерировать типы, законные для использования в союзе (хотя, вероятно, это будет хорошо в варианте).

2

C++ код здесь:

http://svn.boost.org/svn/boost/sandbox/variadic_templates/boost/composite_storage/pack/container_one_of_maybe.hpp

действительно помеченный союз в том, что он может содержать повторяющиеся типы. Одна приятная функция - это теги, которые могут быть перечислены; следовательно, теги могут иметь значащие имена.

К сожалению, время компиляции довольно плохое, я думаю, потому что реализация использует рекурсивное наследование. OTOH, возможно, компиляторы в конечном итоге выяснят способ , чтобы уменьшить затраты времени на компиляцию.

OTOH, если вы хотите придерживаться boost :: variant, вы можете обернуть типы, , как предложил Mark B. Однако вместо описательных имен класса Mark B , которые требуют некоторой мысли, вы можете использовать fusion::pair<mpl::int_<tag>,T_tag> , где T_tag является тегом-элементом в источнике fusion::vector. IOW:

variant 
< fusion::pair<mpl::int_<1>,T1> 
, fusion::pair<mpl::int_<2>,T2> 
... 
, fusion::pair<mpl::int_<n>,Tn> 
> 

Как слитых документы:

http://www.boost.org/doc/libs/1_55_0/libs/fusion/doc/html/fusion/support/pair.html

говорят, fusion::pair только выделяет пространство для 2-го аргумента шаблона; следовательно, это не должно занимать больше места, чем boost::variant<T1,T2,...,Tn>.

HTH.

-regards, Ларри

1

Вы не можете в данный момент, но C++17's implementation of std::variant к счастью, это позволяет:

Вариант разрешается держать тот же тип более чем один раз, и провести по-разному резюме квалифицированных версии того же типа.

В отличие от версии наддува, вы можете получить значения по индексу, что-то вроде этого (не проверено):

// Construct a variant with the second value set. 
variant<string, string, string> s(std::in_place_index<1>, "Hello"); 
// Get the second value. 
string first = std::get<1>(s); 

Майкл Парк написал a C++14 implementation of C++17's std::variant.