如何解决在 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 是 4 个元素的列表 - 1,2,3,4。
IntList<1,4>::head; //Should be 1
IntList<1,4>::size; //Should be 4
IntList<1,4>::next; //Should be IntList<2,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,3>,IntList<>,IntList<4,5>>::list; //Should be IntList<1,4,5>
ConcatedIntLists<IntList<1>,IntList<2>,IntList<3>,IntList<4>>::list; //Should be IntList<1,4>
这是我陷入困境的部分。
解决方法
这个解决方案是对我的原始解决方案的改进,由 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。
,这类事情可以通过对类型列表进行左折叠来解决。 由于我们无法迭代可变参数模板参数包,因此我们需要求助于递归。 如果您不习惯模板元编程,那么代码可能会有点难以理解,因此我在代码示例中添加了一些解释性注释。
// 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 T>
struct LeftFold<Func,T> {
typedef typename Func<State,T>::type type;
};
template<typename L0,typename... Ls>
struct ConcatenateIntLists {
typedef typename LeftFold<ConcateIntListsFunctor,L0,Ls...>::type type;
};
现场示例here。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。