在 C++11 中使用模板元编程连接列表

Concatenating lists using template metaprogramming in C++11

我创建了一个名为 IntList 的新类型,它表示一个整数列表。这是使用模板制作的:

template<int...>
struct IntList;

template<int h, int... t>
struct IntList<h, t...>{
    constexpr static int head = h;
    typedef IntList<t...> next;
    constexpr static int size = sizeof...(t) + 1;
    constexpr static bool empty = false;
};

template<>
struct IntList<>{
    constexpr static int size = 0;
    constexpr static bool empty = true;
};

例如,IntList<1,2,3,4> 是一个包含 4 个元素的列表 - 1,2,3,4。

IntList<1,2,3,4>::head; //Should be 1
IntList<1,2,3,4>::size; //Should be 4
IntList<1,2,3,4>::next; //Should be IntList<2,3,4>

现在,我想使用模板创建一个新类型来连接这些类型的列表。它将被称为 ConcatedIntLists。 如果我只需要连接两个列表,那就很简单了:

template<typename...>
struct ConcatedIntLists;

template<int...T1, int...T2>
struct ConcatedIntLists<IntList<T1...>, IntList<T2...>>{
    typedef IntList<T1..., T2...> list;
};

但是如果我想连接未知数量的列表怎么办?例如:

ConcatedIntLists<IntList<1,2,3>, IntList<>, IntList<4,5>>::list; //Should be IntList<1,2,3,4,5>
ConcatedIntLists<IntList<1>, IntList<2>, IntList<3>, IntList<4>>::list; //Should be IntList<1,2,3,4>

这是我卡住的部分。

这类事情可以通过对类型列表进行左折叠来解决。 由于我们无法迭代可变模板参数包,因此我们需要求助于递归。 如果您不习惯模板元编程,代码可能会有点让人不知所措,所以我在代码示例中添加了一些解释性注释。

// This is the functor we want to fold the elements over.
// It simply concatenates two IntLists.
template<typename L1, typename L2>
struct ConcateIntListsFunctor;

// The actual functor is a so called meta function, it's arguments
// are the template parameters in the primary template. 
template<int...T1, int...T2>...
struct ConcateIntListsFunctor<IntList<T1...>, IntList<T2...>>{
    // ... and we "call" it by retrieving it's "type" typedef.
    typedef IntList<T1..., T2...> type;
};


template<template<typename, typename> class, typename...>
struct LeftFold;

// The LeftFold is a meta-function that takes a binary meta-function as first parameter, a State and a list of types we want to fold over.
template<template<typename, typename> class Func,  typename State, typename T, typename... Ts>
struct LeftFold<Func, State, T, Ts...> {
    typedef typename Func<State, T>::type NewState; 
    // Here is where the recursion happens.
    typedef typename LeftFold<Func, NewState, Ts...>::type type;  
};

template<template<typename, typename> class Func, typename State, typename T>
struct LeftFold<Func, State, T> {
    typedef typename Func<State, T>::type type;
};

template<typename L0, typename... Ls>
struct ConcatenateIntLists {
    typedef typename LeftFold<ConcateIntListsFunctor, L0, Ls...>::type type;
};

实例here.

这个解决方案是对我原来的解决方案的改进,由 Jarod42 在评论中建议。

您可以递归地编写此模板:

template<typename ...T>
struct ConcatedIntLists;  // just to allow specializations
                          // for base case, and general case 

template<typename T>
struct ConcatedIntLists<T> 
{
    using list = T;             // base case: just a list
};

// for 2 or more lists
template<int...T1, int...T2, typename ...CRest>
struct ConcatedIntLists<IntList<T1...>, IntList<T2...>, CRest...>    
{
    using list = typename ConcatedIntLists<IntList<T1..., T2...> , CRest...>::list;
                                       //  ^concatenate first two, ^and the rest      
}

这是一个demo

请注意,此解决方案使用 using 语法而不是 typedef 语法,因为它更易于阅读。

您可以添加这个额外的专业:

template<int...T1, typename...Ts>
struct ConcatedIntLists<IntList<T1...>, Ts...> {
    typedef typename ConcatedIntLists<IntList<T1...>,
                                      typename ConcatedIntLists<Ts...>::list>::list list;
};

Demo.