首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从给定的嵌套boost变体类型创建新的boost变体类型。

从给定的嵌套boost变体类型创建新的boost变体类型。
EN

Stack Overflow用户
提问于 2016-09-01 13:01:41
回答 2查看 1.6K关注 0票数 7

假设我有一个嵌套的boost::variant-type TNested,它包含一些类型和一些其他boost::variant类型(它本身不能再次包含boost::variant types,这样就不会有递归)。

我正在寻找一个模板别名flatten<TNested>,它将计算为一个boost::variant类型,没有嵌套的boost::variant,例如TFlatten,而可能的重复类型正在被删除,例如int只发生一次。

我真的不知道,是否可以做到这一点。

代码语言:javascript
复制
#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;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-01 13:50:27

wandbox example

下面是一个有效的基于C++11递归模板的解决方案,适用于您的示例:

代码语言:javascript
复制
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将被扁平的类型填充,并在递归结束时返回。
代码语言:javascript
复制
- The recursion ends when `Ts...` is empty.

  • 不变体类型被附加到TResult
  • 变体类型是解压缩的,它们的所有内部类型都是递归地扁平的,然后附加到TResult中。

执行情况:

代码语言:javascript
复制
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;
票数 6
EN

Stack Overflow用户

发布于 2016-09-01 13:44:36

boost::variant的问题在于它不是真正的可变模板,它只是通过添加一些具有默认值boost::detail::variant::void_的模板参数来模拟一个模板。要像使用各种模板一样使用它,首先需要将其更改为具有相关类型的包。完整的c++11可以使用方法来做您想做的事情,如下所示。该方法既考虑了平面度,又考虑了结果类型的唯一性假设。如果您决定使用integer_sequence,则无需使用c++14部件:

代码语言:javascript
复制
#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");
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39272268

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档