如何使用Variadic模板展平类型树?

odi*_*erd 9 c++ templates metaprogramming variadic-templates c++11

我有这样的结构:

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

typedef List<char,List<int,float,List<int,unsigned char>>,List<unsigned,short>> MyList;
Run Code Online (Sandbox Code Playgroud)

我想基本上把它压扁到一个列表.什么是最好的方法?如果我把它弄得足够长,我想我可以通过递归制作一些东西,但有些东西告诉我应该有更好的方法.

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

typedef List<char,int,float,int,unsigned char,unsigned,short> FlattenedList;
Run Code Online (Sandbox Code Playgroud)

这是我的第一次尝试:

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;
};
Run Code Online (Sandbox Code Playgroud)

但它会导致此错误: error C3515: if an argument for a class template partial specialization is a pack expansion it shall be the last argument

rici在这里指出MSVC2013在抱怨什么,所以这里没有编译错误:

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

— A function parameter pack that does not occur at the end of the parameter-declaration-clause.
Run Code Online (Sandbox Code Playgroud)

更新:

我想可以在最后放入一个虚拟参数,继续将第一个参数移动到结尾或者将它扩展到前面,如果它是一个List并且专注于第一个参数是我的虚拟来停止递归.对于编译器而言,这似乎只是为了压缩列表而做了很多工作.

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;
};
Run Code Online (Sandbox Code Playgroud)

这适用于MSVC2013,但我不认为它是最好的方法,因为我需要一个虚拟类型,它会给编译器带来很多负担.我想将它与包含500多个元素的列表一起使用.

cat*_*dle 3

另一种方法是使用辅助类和累加器列表而不是MyMagicType. 我们从一个空开始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");
}
Run Code Online (Sandbox Code Playgroud)