使用预先编写的包转换更改模板包的一部分

Changing part of a template pack using a pre-written pack transformation

假设你有类似的东西

template <typename, typename, int, typename, int, typename...> struct P

并且您只想反转 typename... 部分。现在您已经编写了通用反向转换:

// Reverse<Pack<Types...>>::type is Pack<Types'...>, where Types'... is Types... reversed.
template <typename, typename> struct ReverseHelper;

template <template <typename...> class P, typename Pack>
struct ReverseHelper<P<>, Pack> {
    using type = Pack;
};

template <template <typename...> class P, typename First, typename... Rest, typename... Types>
struct ReverseHelper<P<First, Rest...>, P<Types...>> : ReverseHelper<P<Rest...>, P<First, Types...>> {};

template <typename> struct Reverse;

template <template <typename...> class P, typename... Types>
struct Reverse<P<Types...>> : ReverseHelper<P<Types...>, P<>> {};

当然,我们可以用template <typename, typename, int, typename, int, typename...> class P改写上面的内容,即:

template <typename, typename> struct ReverseHelper1;

template <template <typename, typename, int, typename, int, typename...> class P,
    typename U, typename V, int M, typename W, int N, typename Pack>
struct ReverseHelper1<P<U,V,M,W,N>, Pack> {
    using type = Pack;
};

template <template <typename, typename, int, typename, int, typename...> class P,
    typename U, typename V, int M, typename W, int N, typename First, typename... Rest, typename... Types>
struct ReverseHelper1<P<U,V,M,W,N, First, Rest...>, P<Types...>> : ReverseHelper<P<U,V,M,W,N, Rest...>, P<First, Types...>> {};

template <typename> struct Reverse1;

template <template <typename, typename, int, typename, int, typename...> class P,
    typename U, typename V, int M, typename W, int N, typename... Types>
struct Reverse1<P<U,V,M,W,N, Types...>> : ReverseHelper1<P<U,V,M,W,N, Types...>, P<U,V,M,W,N>> {};

注意到我们只是在重复吗?然后如果我们想做同样的部分逆转事情,我们将不得不对其他模板签名一次又一次地这样做。那么如何通过使用原始 Reverse 本身来避免所有这些重复?

例如,假设我们有

template <typename> struct Foo;
template <typename> struct Bar;

template <template <typename, typename, int, typename, int, typename...> class P,
    typename U, typename V, int M, typename W, int N, typename... Args>
struct Foo<P<U,V,M,W,N, Args...>> {};

让我们从 Bar<P<U,V,M,W,N, ArgsReversed...>> 派生 Foo<P<U,V,M,W,N, Args...>>。如何使用上面定义的 Reverse 来完成此操作?

注意,这与

不同
template <template <typename, typename, int, typename, int, typename> class P,
    typename U, typename V, int M, typename W, int N,
    template <typename...> class Q, typename... Args>
struct Foo<P<U,V,M,W,N, Q<Args...>>> : Bar<P<U,V,M,W,N, typename Reverse<Q<Args...>>::type>> {};

虽然我怀疑完成它是按照这个思路完成的。当然,倒车只是一个例子。我们希望重用任何转换以仅对(任何)较大模板结构的一部分进行相同的转换。

让我们制作一个类型列表:

template <typename...> struct typelist { };

你可以得到typelist的第N个类型:

template <size_t N, typename> struct typelist_get;

template <typename T, typename ...Ts>
struct typelist_get<0, typelist<T, Ts...>>
{
  using type = T;
};

template <size_t N, typename T, typename ...Ts>
struct typelist_get<N, typelist<T, Ts...>>
  : typelist_get<N - 1, typelist<Ts...>>
{ };

您可以反转类型列表:

template <typename, typename> struct reverse_helper;

template <typename T, typename ...Ts, typename ...Rs>
struct reverse_helper<typelist<T, Ts...>, typelist<Rs...>>
  : reverse_helper<typelist<Ts...>, typelist<T, Rs...>>
{ };

template <typename ...Rs>
struct reverse_helper<typelist<>, typelist<Rs...>>
{
  using type = typelist<Rs...>;
};

template <typename T> struct typelist_reverse
  : reverse_helper<T, typelist<>>
{ };

我们还需要一个index_sequence:

template <size_t...> struct index_sequence;

以及为给定 N:

构建 index_sequence<0, 1, ..., N - 1> 的方法
template <std::size_t N, std::size_t ...I>
struct index_sequence_builder
{
  using type = typename index_sequence_builder<N - 1, N - 1, I...>::type;
};

template <std::size_t ... I>
struct index_sequence_builder<0, I...>
{
  using type = index_sequence<I...>;
};


template <std::size_t N>
using make_index_sequence = typename index_sequence_builder<N>::type;

假设我们有一些可变参数模板 class Foo:

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

那么我们可以反推如下:

template <typename, typename> struct reverse_foo_impl;

template <typename ...Ts, size_t ...I>
struct reverse_foo_impl<Foo<Ts...>, index_sequence<I...>>
{
  using TL = typelist<Ts...>;
  using RTL = typename typelist_reverse<TL>::type;

  using type = Foo<typename typelist_get<I, RTL>::type...>;
};

template <typename> struct reverse_foo;

template <typename...Ts>
struct reverse_foo<Foo<Ts...>>
  : reverse_foo_impl<Foo<Ts...>, make_index_sequence<sizeof...(Ts)>>
{ };

这里,TLFoo作为typelist的模板参数,RTL是同一个typelist的反转。要将模板参数提取为包,我们需要创建类似 typelist_get<0, RTL>::type, typelist_get<1, RTL>::type, ..., typelist_get<N - 1, RTL>::type 的内容。这是使用扩展 w.r.t 处的索引序列完成的。 I 完全重现了该模式。

最后我们可以这样使用:

  using T = Foo<int, char, double>;

  using R = reverse_foo<T>::type;
  static_assert(std::is_same<Foo<double, char, int>, R>::value, ":(");

简单的方法是停止使用 int 作为模板的参数。

除此之外,您可以为 class 和 int 类型的特定模式编写元函数,并且 "lift" 模板和实例都约为 classes(其中 int 替换为 integral_constant),并对它们进行操作(写 "reverse types after N types" 以完成反向操作)。

除此之外,我们可以为您的特定模式手动编码。

template<class...>struct types{using type=types;};

namespace details {
  template<class T, class types>
  struct replace_tail;
  template<class T, class types>
  using replace_tail_t = typename replace_tail<T,types>::type;

  template<class T>
  struct get_tail;
  template<class T>
  using get_tail_t = typename get_tail<T,types>::type;

  template<template <class, class, int, class, int, class...> class P, 
    class A, class B, int C, class D, int E, class...Fs,
    class... Ts
  >
  struct replace_tail<P<A,B,C,D,E,Fs...>,types<Ts...>> {
    using type = P<A,B,C,D,E,Ts...>;
  };
  template<template <class, class, int, class, int, class...> class P, 
    class A, class B, int C, class D, int E, class...Fs
  >
  struct get_tail<P<A,B,C,D,E,Fs...>>:types<Fs...>{};

  template<class T>
  using reverse_t = ReverseHelper<T>::type;
  template<class T>
  using reverse_tail = replace_tail_t < T, reverse_t<get_tail_t<T>> >;
}

using details::reverse_tail;

其中可能包含语法错误。计划是把它分成 3 个部分。

首先,反转一包(你已经写过了)。

其次,提取 "tail" 参数以从一个实例反转到一个包中。

第三,用另一个包替换"tail"参数。

钩在一起,我们反尾巴。作为 get_tail_treplace_tail_t 的专门化 template 参数的新模式将使 reverse_tail_t "just work".