如何使用Variadic模板来展平类型树? [英] How can I use Variadic templates to flatten a tree of types?

查看:136
本文介绍了如何使用Variadic模板来展平类型树?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个这样的结构:

template<typename... Ts>
struct List {}

typedef List<char,List<int,float,List<int,unsigned char>>,List<unsigned,short>> MyList;

,我想将它基本上平整为一个列表。什么是最好的方法?我想我可以做一些递归的东西,如果我干的足够长,但事情告诉我应该有一个更好的方法。

and I want to essentially flatten it to one list. What is the best way? I think I could make something with recursion if I fiddle with it long enough but something tells me there should be a better way.

我想要的是上述结果tree应该类似于:

What I want as a result of the above tree should be similar to this:

typedef List<char,int,float,int,unsigned char,unsigned,short> FlattenedList;

这是我的第一次尝试:

template<typename... Ts>
struct List{};

template<typename... Ts>
struct FlattenTree{
    typedef List<Ts...> Type;
};
template<typename... Ts, typename... Us, typename... Vs>
struct FlattenTree<Ts...,List<Us...>,Vs...>{
    typedef typename FlattenTree<Ts..., Us..., Vs...>::Type Type;
};

但会导致此错误:错误C3515:类模板部分专业化是一个包扩展,它应该是最后一个参数

but it results in this error: error C3515: if an argument for a class template partial specialization is a pack expansion it shall be the last argument

com / questions / 17332286 / why-does-this-variadic-template-specialization-definition-not-compiletitle =here>这里什么MSVC2013是抱怨,所以没有编译器错误在这里:

rici pointed out here what MSVC2013 is complaining about, so no compiler bug here:

§14.8.2.5(从类型中导出模板参数)第5段列出了无法推导出模板参数的上下文。相关的一个是列表中的最后一个:

§ 14.8.2.5 (Deducing template arguments from a type) paragraph 5 lists the contexts in which template arguments cannot be deduced. The relevant one is the last one in the list:

— A function parameter pack that does not occur at the end of the parameter-declaration-clause.

更新

我想可以在最后添加一个虚拟参数,继续移动第一个参数到结束或扩展到前面,如果它的列表,并专门的第一个参数是我的虚拟,停止递归。这看起来像编译器很多工作,只是为了展平列表。

I guess one could put in a dummy parameter at the very end, keep moving the first argument to the end or expanding it to the front if its a List and specialize on the first parameter being my dummy to stop recursion. That seems like a lot of work for the compiler just to flatten a list though.

namespace Detail{
    struct MyMagicType {};
    template<typename T, typename... Ts>
    struct FlattenTree{
        typedef typename FlattenTree<Ts..., T>::Type Type;
    };
    template<typename... Ts>
    struct FlattenTree<MyMagicType,Ts...>{      //termination case
        typedef List<Ts...> Type;
    };
    template<typename... Ts, typename... Us>
    struct FlattenTree<List<Ts...>, Us...>{
        typedef typename FlattenTree<Ts..., Us...>::Type Type;
    };              //expand Ts to front because they may hold more nested Lists
}

template<typename... Ts>
struct FlattenTree{
    typedef typename Detail::FlattenTree<Ts...,Detail::MyMagicType>::Type Type;
};

这适用于MSVC2013,但我不认为它的最佳方式,因为我需要一个虚拟类型并且它对编译器造成了很大的负担。

This works on MSVC2013 but I don't think its the best way possible since I needed a dummy type and it puts a lot of load on the compiler. I want to use it with lists containing 500+ elements.

推荐答案

另一种方法是使用辅助类和累加器列表 MyMagicType 。我们从空的 List<> 开始,然后从输入列表中填写类型:

Another approach is to use a helper class and an accumulator list instead of MyMagicType. We start with an empty List<> and then fill it with types from the input list:

#include <type_traits>

template <class... Ts> struct List {};

// first parameter - accumulator
// second parameter - input list
template <class T, class U>
struct flatten_helper;

// first case - the head of the List is List too
// expand this List and continue
template <class... Ts, class... Heads, class... Tail>
struct flatten_helper<List<Ts...>, List<List<Heads...>, Tail...>> {
    using type = typename flatten_helper<List<Ts...>, List<Heads..., Tail...>>::type;
};

// second case - the head of the List is not a List
// append it to our new, flattened list
template <class... Ts, class Head, class... Tail>
struct flatten_helper<List<Ts...>, List<Head, Tail...>> {
    using type = typename flatten_helper<List<Ts..., Head>, List<Tail...>>::type;
};

// base case - input List is empty
// return our flattened list
template <class... Ts>
struct flatten_helper<List<Ts...>, List<>> {
    using type = List<Ts...>;
};

// wrapper around flatten_helper
template <class T> struct flatten;

// start with an empty accumulator
template <class... Ts>
struct flatten<List<Ts...>> {
    using type = typename flatten_helper<List<>, List<Ts...>>::type;
};

auto main() -> int {
    using Types = List<int, List<float, List<double, List<char>>>>;
    using Flat = flatten<Types>::type;

    static_assert(std::is_same<Flat, List<int, float, double, char>>::value, "Not the same");
}

这篇关于如何使用Variadic模板来展平类型树?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆