最直接的子问题在类型列表领域:
template <class... Ts>
struct typelist {
using type = typelist;
static constexpr std::size_t size = sizeof...(Ts);
};
template <class T>
struct tag { using type = T; };
template <std::size_t N, class TL>
struct head_n {
using type = ???;
};
现在,
head_n
只是简单递归的问题——从一个空列表开始,将一个元素从一个列表移动到另一个列表
N
次。
template <std::size_t N, class R, class TL>
struct head_n_impl;
template <std::size_t N, class... Ts, class U, class... Us>
struct head_n_impl<N, typelist<Ts...>, typelist<U, Us...>>
: head_n_impl<N-1, typelist<Ts..., U>, typelist<Us...>>
;
template <class... Ts, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<Us...>>
: tag<typelist<Ts...>>
;
template <class... Ts, class U, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<U, Us...>>
: tag<typelist<Ts...>>
;
template <std::size_t N, class TL>
using head_n = typename head_n_impl<N, typelist<>, TL>::type;
从这个问题到你的具体问题,我将留给读者自己思考。
另一种方法是通过连接。将typelist<Ts...>
的每个元素转换为typelist<T>
或typelist<>
,然后将它们全部连接在一起。concat
很简单:
template <class... Ts>
struct concat { };
template <class TL>
struct concat<TL>
: tag<TL>
{ };
template <class... As, class... Bs, class... Rest>
struct concat<typelist<As...>, typelist<Bs...>, Rest...>
: concat<typelist<As..., Bs...>, Rest...>
{ };
然后我们可以这样做:
template <std::size_t N, class TL, class = std::make_index_sequence<TL::size>>
struct head_n;
template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
: concat<
std::conditional_t<(Is < N), typelist<Ts>, typelist<>>...
>
;
template <std::size_t N, class TL>
using head_n_t = typename head_n<N, TL>::type;
这种方法的优点在于,可以在C++17中使用折叠表达式代替
concat
,只需提供适当的
operator+
。
template <class... As, class... Bs>
constexpr typelist<As..., Bs...> operator+(typelist<As...>, typelist<Bs...> ) {
return {};
}
这使得:
template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
{
using type = decltype(
(std::conditional_t<(Is < N), typelist<Ts>, typelist<>>{} + ... + typelist<>{})
);
};