Дискриминируемый профсоюзный контейнер на некоторых типах определяется путем мгновенных boost::variant шаблон класса с желаемыми типами. Эти типы называются привязанными типами и подчиняются требованиям концепции BoundedType. Любое количество ограниченных типов может быть указано, вплоть до определенного предела реализации (см. BOOST_VARIANT_LIMIT_TYPES.
Например, следующий объявляет дискриминируемый профсоюзный контейнер на int и std::string:
По умолчанию variant по умолчанию устанавливает свой первый ограниченный тип, поэтому v изначально содержит int(0). Если это не желаемо, или если первый ограниченный тип не является структурированным по умолчанию, a variant может быть построен непосредственно от любого значения, конвертируемого в один из его ограниченных типов. Точно так же вариант может быть присвоен любое значение конвертируемое к одному из его ограниченных типов, как показано в следующем:
v = "hello";
Теперь v содержит std::string, равный "hello". Мы можем продемонстрировать это потокv для стандартного вывода:
std::cout << v << std::endl;
Обычно, однако, мы хотели бы сделать больше с содержанием variant, чем потоковой. Таким образом, нам нужен какой-то способ получить доступ к содержащейся стоимости. Есть два способа достичь этого: apply_visitor, который является самым безопасным и очень мощным, и get, который иногда удобнее использовать.
Например, предположим, что мы хотели концентрировать строку, содержащуюся в v. С восстановление стоимости на get, это может быть достигнуто довольно просто, как показано в следующем:
По желанию std::string, содержащийся в v, теперь равен "hello world! ". Опять же, мы можем продемонстрировать это, потоковая передача v для стандартного вывода:
std::cout << v << std::endl;
Хотя использование get является вполне приемлемым в этом тривиальном примере, get обычно страдает от нескольких значительных недостатков. Например, если бы мы написали функцию, принимающую variant, мы бы не знали, содержит ли пройденный variantint или std::string. Если бы мы настаивали на продолжении использования get, нам нужно было бы запросить variant для его содержащегося типа. Следующая функция, которая «удвояет» содержание данного вариант, демонстрирует такой подход:
Тем не менее, такой код довольно хрупкий, и без тщательного внимания, скорее всего, приведет к внедрению тонких логических ошибок, обнаруживаемых только в рабочее время. Например, рассмотрим, хотим ли мы расширить times_two для работы на variant с дополнительными связанными типами. В частности, std::complex<ДП> в комплект. Очевидно, нам нужно хотя бы изменить декларацию функций:
Конечно, необходимы дополнительные изменения, поскольку в настоящее время, если пройденный вариант фактически содержит значение std::complex, times_two будет молча возвращаться - без каких-либо желаемых побочных эффектов и без какой-либо ошибки. В этом случае исправление очевидно. Но в более сложных программах может потребоваться значительное время, чтобы определить и обнаружить ошибку в первую очередь.
Таким образом, использование в реальном мире variant, как правило, требует более надежного механизма доступа, чем get. По этой причине вариант поддерживает проверенное время компиляции визитация через apply_visitor. Посещение требует, чтобы программист явно обрабатывал (или игнорировал) каждый ограниченный тип. Неспособность сделать это приводит к ошибке компиляции времени.
Посещение вариант требует объекта посетителя. Ниже показана одна такая реализация посетителя, реализующего поведение, идентичное times_two:
class times_two_visitor
: public boost::static_visitor<>
{
public:
void operator()(int & i) const
{
i *= 2;
}
void operator()(std::string & str) const
{
str += str;
}
};
После реализации вышеуказанного посетителя мы можем применить его к v, как видно из следующего:
Как и ожидалось, содержание v сейчас является std::string равным "hello world! Привет, мир! ". (На этот раз мы пропустим проверку.)
В дополнение к повышенной надежности, посещение предоставляет еще одно важное преимущество над get: способность писать общие посетители. Например, следующий посетитель «удвоит» содержание anyвариант (при условии его ограниченных типов каждый оператор поддержки+=):
class times_two_generic
: public boost::static_visitor<>
{
public:
template <typename T>
void operator()( T & operand ) const
{
operand += operand;
}
};
Опять же, apply_visitor устанавливает колеса в движении:
Хотя первоначальные затраты на установку посещения могут превысить затраты, требуемые для get, преимущества быстро становятся значительными. Прежде чем завершить этот раздел, мы должны изучить одно последнее преимущество посещения с apply_visitor: задержка посещения. А именно, доступна специальная форма apply_visitor, которая не сразу применяет данного посетителя к любому variant, а скорее возвращает объект функции, который работает на любом variant, заданном ему. Это поведение особенно полезно при работе на последовательности вариант тип, как показано ниже:
В этом разделе обсуждаются несколько особенностей библиотеки, часто необходимых для расширенного использования variant. В отличие от приведенного выше раздела, каждая функция, представленная ниже, в значительной степени независима от других. Соответственно, этот раздел не обязательно должен читаться линейно или полностью.
Preprocessor macros
В то время как вариатический список параметров шаблона variant значительно упрощает использование для конкретных мгновенных сообщений шаблона, это значительно усложняет использование для генерических мгновенных сообщений. Например, в то время как сразу ясно, как можно написать функцию, принимающую конкретную вариантную мгновенную передачу, скажем вариант, менее ясно, как можно написать функцию, принимающую любой заданный вариант.
Из-за отсутствия поддержки подлинных вариатических списках параметров шаблона в стандарте C++98, необходим препроцессор. В то время как препроцессорная библиотека обеспечивает общее и мощное решение, необходимость повторения BOOST_VARIANT_LIMIT_TYPES без необходимости загромождает простой код. Поэтому для общих вариантов использования эта библиотека предоставляет свой собственный макрос BOOST_VARIANT_ENUM_PARAMS.
Этот макрос упрощает для пользователя процесс объявления типов variant в шаблонах функций или явной частичной специализации шаблонов класса, как показано в следующем:
В то время как это удобно для типичных применений, список параметров variant классного шаблона ограничивает в двух существенных размерах. Во-первых, из-за отсутствия поддержки подлинных вариатических списков параметров шаблона на C++ количество параметров должно быть ограничено определенным максимумом реализации (а именно, BOOST_VARIANT_LIMIT_TYPES). Во-вторых, характер списков параметров в целом делает манипулирование списками в компиляцию-время чрезмерно сложным.
Чтобы решить эти проблемы, make_variant_over< Последовательность > выставляет variant, чьи ограниченные типы являются элементами Sequence (где Sequence является любым типом, отвечающим требованиям MPL Sequence). Например,
Портируемость: К сожалению, из-за стандартных вопросов соответствия в нескольких компиляторах make_variant_over не является универсально доступным. На этих компиляторах библиотека указывает на отсутствие поддержки синтаксиса через определение предпроцессорного символа BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT.
Рекурсивные типы облегчают построение сложной семантики из простого синтаксиса. Например, почти каждый программист знаком с каноническим определением реализации связанного списка, чье простое определение позволяет последовательности неограниченной длины:
Характер вариант как шаблон общего класса, к сожалению, исключает простое построение рекурсивных вариант Типы. Рассмотрим следующую попытку построить структуру для простых математических выражений:
В то время как хорошо намерения, вышеупомянутый подход не будет компилироваться, потому что binary_op все еще является неполным, когда variant тип expression мгновенный. Кроме того, подход страдает от более значительного логического недостатка: даже если бы синтаксис C++ был другим так, что приведенный выше пример мог бы быть сделан для «работы», выражение должно быть бесконечного размера, что явно невозможно.
Поскольку variant предоставляет специальную поддержку recursive_wrapper, клиенты могут рассматривать результат variant, как если бы обертка не присутствовала. Это видно при реализации следующего посетителя, который рассчитывает значение выражения без ссылки на recursive_wrapper:
Последовательность: boost::recursive_wrapper не имеет пустого состояния, что делает его конструктор движения не очень оптимальным. Рассмотрим использование std::unique_ptr или другого безопасного указателя для лучшей производительности на C++11 совместимых компиляторах.
Recursive types with make_recursive_variant
Для некоторых применений рекурсивных вариантных типов пользователь может пожертвовать полной гибкостью использования recursive_wrapper с variant для следующего удобного синтаксиса:
Портируемость: К сожалению, из-за стандартных вопросов соответствия в нескольких компиляторах make_recursive_variant не пользуется универсальной поддержкой. На этих компиляторах библиотека указывает на отсутствие поддержки через определение предпроцессорного символа BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT. Таким образом, если не работать с высокосоответствующими компиляторами, максимальная переносимость будет достигнута за счет использования recursive_wrapper, как описано в разделе под названием “Recursive types with recursive_wrapper”.
Binary visitation
Как показывает вышеупомянутый учебник, посещение является мощным механизмом для манипулирования контентом variant. Бинарные посещения далее расширяет силу и гибкость посещения, позволяя одновременно посещать содержание двух разных объектов вариант.
Примечательно, что эта функция требует, чтобы бинарные посетители были несовместимы с объектами посетителей, обсуждаемыми в учебнике выше, поскольку они должны работать на двух аргументах. Ниже показана реализация бинарных посетителей:
class are_strict_equals
: public boost::static_visitor<bool>
{
public:
template <typename T, typename U>
bool operator()( const T &, const U & ) const
{
return false; // cannot compare different types
}
template <typename T>
bool operator()( const T & lhs, const T & rhs ) const
{
return lhs == rhs;
}
};
Как и ожидалось, посетитель применяется к двум аргументам variant с помощью apply_visitor:
Наконец, мы должны отметить, что объект функции вернулся из «задержанной» формы apply_visitor, как показано ниже:
typedef boost::variant<double, std::string> my_variant;
std::vector< my_variant > seq1;
seq1.push_back("pi is close to ");
seq1.push_back(3.14);
std::list< my_variant > seq2;
seq2.push_back("pi is close to ");
seq2.push_back(3.14);
are_strict_equals visitor;
assert( std::equal(
seq1.begin(), seq1.end(), seq2.begin()
, boost::apply_visitor( visitor )
) );
Multi visitation
Многократное посещение расширяет силу и гибкость посещения, позволяя одновременно посещать содержание трех и более различных объектов вариант. Обратите внимание, что заголовок для нескольких посетителей должен быть включен отдельно.
Примечательно, что эта функция требует, чтобы много посетителей несовместимы с объектами посетителей, обсуждаемыми в учебнике выше, поскольку они должны работать на том же потоке аргументов, которые были переданы apply_visitor. Ниже показана реализация мультипосетителя по трем параметрам:
Статья Tutorial раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 42. Boost.Variant может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.