假设我有一个嵌套的boost::variant
- 类型TNested
包含一些类型和一些其他boost::variant
类型(它们本身不能再包含boost::variant types
,所以会有没有递归)。
我正在寻找一个模板别名flatten<TNested>
,评估为boost::variant
类型没有嵌套boost::variant
,例如TFlatten
,而正在删除可能的重复类型,例如int
只发生一次。
我真的不知道,如果可以某种方式完成。
#include <boost/variant.hpp>
#include <boost/any.hpp>
#include <iostream>
struct Person;
typedef boost::variant<int, double, boost::variant<std::string, int>, boost::variant<Person>> TNested;
typedef boost::variant<int, double, std::string, Person> TFlatten;
template<typename NestedVariant>
using flatten = //???
int main() {
flatten<TNested> x;
std::cout << typeid(x) == typeid(TFlatten) << std::endl;
}
答案 0 :(得分:4)
这是一个有效的C ++ 11递归模板解决方案,适用于您的示例:
using nested = variant<int, double, variant<std::string, int>, variant<Person>>;
using flattened = variant<int, double, std::string, int, Person>;
static_assert(std::is_same<flatten_variant_t<nested>, flattened>{}, "");
总体思路:
std::tuple
用作泛型类型列表,std::tuple_cat
用于连接类型列表。
定义了flatten_variant<TResult, Ts...>
递归元函数,它执行以下操作:
TResult
将填充扁平类型,并将在递归结束时返回。
Ts...
为空时,递归结束。非变体类型会附加到TResult
。
解压缩变体类型,并递归展平其所有内部类型,然后附加到TResult
。
实现:
namespace impl
{
// Type of the concatenation of all 'Ts...' tuples.
template <typename... Ts>
using cat = decltype(std::tuple_cat(std::declval<Ts>()...));
template <typename TResult, typename... Ts>
struct flatten_variant;
// Base case: no more types to process.
template <typename TResult>
struct flatten_variant<TResult>
{
using type = TResult;
};
// Case: T is not a variant.
// Return concatenation of previously processed types,
// T, and the flattened remaining types.
template <typename TResult, typename T, typename... TOther>
struct flatten_variant<TResult, T, TOther...>
{
using type = cat<TResult, std::tuple<T>,
typename flatten_variant<TResult, TOther...>::type>;
};
// Case: T is a variant.
// Return concatenation of previously processed types,
// the types inside the variant, and the flattened remaining types.
// The types inside the variant are recursively flattened in a new
// flatten_variant instantiation.
template <typename TResult, typename... Ts, typename... TOther>
struct flatten_variant<TResult, variant<Ts...>, TOther...>
{
using type =
cat<TResult, typename flatten_variant<std::tuple<>, Ts...>::type,
typename flatten_variant<TResult, TOther...>::type>;
};
template<typename T>
struct to_variant;
template<typename... Ts>
struct to_variant<std::tuple<Ts...>>
{
using type = variant<Ts...>;
};
}
template <typename T>
using flatten_variant_t =
typename impl::to_variant<
typename impl::flatten_variant<std::tuple<>, T>::type
>::type;
答案 1 :(得分:2)
boost::variant
的问题在于,它不是真正的可变参数模板,只是通过添加一些默认值为boost::detail::variant::void_
的模板参数来模拟一个。要像使用可变参数模板一样使用它,需要先将其更改为具有相关类型的包。完整的c ++ 11即用型方法可以实现您想要的功能,如下所示。该方法考虑了结果类型的平坦性和唯一性假设。如果您决定使用c ++ 14:
integer_sequence
部分将是不必要的
#include <boost/variant.hpp>
#include <tuple>
#include <type_traits>
template <class T, T... Vs>
struct integer_sequence { };
template <class T, class, class, class = integer_sequence<T>, class = integer_sequence<T, 0>, class = void>
struct make_integer_sequence_impl;
template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 0>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, typename std::enable_if<(ICV1 > 0)>::type>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Res...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };
template <class T, T ICV1, T... Res, T... Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, ICV1>, std::integral_constant<T, 1>, integer_sequence<T, Res...>, integer_sequence<T, Pow...>, void>: make_integer_sequence_impl<T, std::integral_constant<T, ICV1/2>, std::integral_constant<T, ICV1%2>, integer_sequence<T, Pow..., (Res + sizeof...(Pow))...>, integer_sequence<T, Pow..., (Pow + sizeof...(Pow))...>> { };
template <class T, class Res, class Pow>
struct make_integer_sequence_impl<T, std::integral_constant<T, 0>, std::integral_constant<T, 0>, Res, Pow, void> {
using type = Res;
};
template <class T, T V>
using make_integer_sequence = typename make_integer_sequence_impl<T, std::integral_constant<T, V/2>, std::integral_constant<T, V%2>>::type;
template <class>
struct is_variant_void {
static constexpr size_t value = 0;
};
template <>
struct is_variant_void<boost::detail::variant::void_> {
static constexpr size_t value = 1;
};
constexpr size_t sum(size_t v){
return v;
}
template <class... Args>
constexpr size_t sum(size_t v, Args... vs){
return v + sum(vs...);
}
template <class Variant>
struct variant_size;
template <class... Ts>
struct variant_size<boost::variant<Ts...>> {
static constexpr size_t value = sizeof...(Ts) - sum(is_variant_void<Ts>::value...);
};
template <class...>
struct pack { };
template <class V, class=make_integer_sequence<size_t, variant_size<V>::value>>
struct variant_to_pack;
template <class... Ts, size_t... Is>
struct variant_to_pack<boost::variant<Ts...>, integer_sequence<size_t, Is...>> {
using type = pack<typename std::tuple_element<Is, std::tuple<Ts...>>::type...>;
};
template <class>
struct unique_opaque { };
template <class... Ts>
struct unique_pack: unique_opaque<Ts>... { };
template <class, class, class = void>
struct unique;
template <class... Res, class T, class... Ts>
struct unique<unique_pack<Res...>, pack<T, Ts...>, typename std::enable_if<std::is_base_of<unique_opaque<T>, unique_pack<Res...>>::value>::type>:
unique<unique_pack<Res...>, pack<Ts...>> { };
template <class... Res, class T, class... Ts>
struct unique<unique_pack<Res...>, pack<T, Ts...>, typename std::enable_if<!std::is_base_of<unique_opaque<T>, unique_pack<Res...>>::value>::type>:
unique<unique_pack<Res..., T>, pack<Ts...>> { };
template <class... Res>
struct unique<unique_pack<Res...>, pack<>, void> {
using type = boost::variant<Res...>;
};
template <class...>
struct flatten;
template <class... Res, class T, class... Rest>
struct flatten<pack<Res...>, T, Rest...>: flatten<pack<Res..., T>, Rest...> { };
template <class... Res, class... Vs, class... Rest>
struct flatten<pack<Res...>, boost::variant<Vs...>, Rest...>: flatten<pack<Res...>, typename variant_to_pack<boost::variant<Vs...>>::type, Rest...> {};
template <class... Res, class... Vs, class... Rest>
struct flatten<pack<Res...>, pack<Vs...>, Rest...>: flatten<pack<Res...>, Vs..., Rest...> { };
template <class Res>
struct flatten<Res>:unique<unique_pack<>, Res> { };
int main() {
boost::variant<int, boost::variant<float, int, boost::variant<char, bool>, bool>> vif;
static_assert(std::is_same<flatten<pack<>, decltype(vif)>::type, boost::variant<int, float, char, bool>>::value, "Test");
}