Ответ 1
EDIT: Boost теперь поддерживает multi-visitation, как и Вариант С++ 17.
Если у вас есть тип варианта с унарной функцией-членом visit
, это можно расширить до функции n-ary-apply_visitor
следующим образом:
Включить необходимые стандартные зависимости библиотеки:
#include <tuple>
#include <type_traits>
#include <utility> //For C++14 `std::integer_sequence`.
//If you don't want to use C++14, write your own.
Теперь вспомогательная функция для создания нового кортежа, идентичного существующему кортежу, но без первого элемента:
template<std::size_t ...S, typename Head, typename ...Tail>
std::tuple<Tail...> tuple_tail_impl(
index_sequence<S...>,
std::tuple<Head, Tail...> const &in_tuple)
{
struct In {
template<std::size_t N>
using ElementType =
typename std::tuple_element<N, std::tuple<Head, Tail...>>::type;
};
return std::tuple<Tail...>(
std::forward<In::ElementType<S+1>>(std::get<S+1>(in_tuple))...);
}
template<typename Head, typename ...Tail>
std::tuple<Tail...> tuple_tail(std::tuple<Head, Tail...> const& in_tuple) {
return tuple_tail_impl(index_sequence_for<Tail...>(), in_tuple);
}
Теперь класс, выполняющий эту работу, и вспомогательную функцию для создания этого класса:
template<typename Visitor, typename MatchedValueTuple, typename... TailVariants>
struct NAryVisitorFlattener;
template<typename Visitor, typename MatchedValueTuple, typename... TailVariants>
NAryVisitorFlattener<Visitor, MatchedValueTuple, TailVariants...>
make_NAryVisitorFlattener(
Visitor &&visitor,
MatchedValueTuple &&matchedValues,
std::tuple<TailVariants...> &&tailVariants);
В рекурсивном случае NAryVisitorFlattener
последовательно вызывает функцию-член apply
для каждого варианта и создает результирующие значения в MatchedValueTuple
.
template<
typename Visitor,
typename MatchedValueTuple,
typename CurrentVariant,
typename... TailVariants>
struct NAryVisitorFlattener<
Visitor, MatchedValueTuple, CurrentVariant, TailVariants...>
{
typedef typename
std::remove_reference<Visitor>::type::result_type result_type;
Visitor visitor;
MatchedValueTuple matchedValues;
std::tuple<CurrentVariant, TailVariants...> tailVariants;
template<typename A>
result_type operator()(A &&a)
{
auto flattener = make_NAryVisitorFlattener(
std::forward<Visitor>(visitor),
std::tuple_cat(matchedValues, std::forward_as_tuple(std::forward<A>(a))),
tuple_tail(tailVariants));
return std::forward<CurrentVariant>(std::get<0>(tailVariants))
.visit(flattener);
}
};
В базовом случае на каждый вариант вызывается apply
, а посетитель вызывается со значениями в MatchedValueTuple
:
template<typename Visitor, typename MatchedValueTuple>
struct NAryVisitorFlattener<Visitor, MatchedValueTuple> {
typedef typename
std::remove_reference<Visitor>::type::result_type result_type;
Visitor visitor;
MatchedValueTuple matchedValues;
std::tuple<> tailVariants;
template<typename A>
result_type operator()(A &&a) {
return callFunc(
std::make_index_sequence<std::tuple_size<MatchedValueTuple>::value>(),
std::forward<A>(a));
}
template<std::size_t N>
using MatchedValueType =
typename std::tuple_element<N,MatchedValueTuple>::type;
template<std::size_t ...S, typename A>
result_type callFunc(std::index_sequence<S...>, A &&a) {
return std::forward<Visitor>(visitor)(
std::forward<MatchedValueType<S>>(matchedValues))...,
std::forward<A>(a));
}
};
И определение вспомогательной функции, объявленное ранее:
template<typename Visitor, typename MatchedValueTuple, typename... TailVariants>
NAryVisitorFlattener<Visitor, MatchedValueTuple, TailVariants...>
make_NAryVisitorFlattener(
Visitor &&visitor,
MatchedValueTuple &&matchedValues,
std::tuple<TailVariants...> &&tailVariants)
{
return {
std::forward<Visitor>(visitor),
std::forward<MatchedValueTuple>(matchedValues),
std::forward<std::tuple<TailVariants...>>(tailVariants)
};
}
Теперь функция, которую вы ждали. Возьмите шарик с первым NAryVisitorFlattener
:
template<typename Visitor, typename VariantA, typename... Variants>
typename std::remove_reference<Visitor>::type::result_type
apply_visitor(Visitor &&visitor, VariantA &&variantA, Variants &&...variants) {
auto flattener = make_NAryVisitorFlattener(
std::forward<Visitor>(visitor),
std::tuple<>{},
std::forward_as_tuple(std::forward<Variants>(variants)...));
return std::forward<VariantA>(variantA).visit(flattener);
}
Это все взято из моей полной версии совместимого варианта с С++ 11 здесь.