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

时间:2015-03-05 12:54:23

标签: c++ templates c++11 variadic

假设您有类似

的内容
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...>> {};

Foo<P<U,V,M,W,N, Args...>>来自Bar<P<U,V,M,W,N, ArgsReversed...>>。如何使用上面定义的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>> {};

虽然我怀疑完成它是做了类似的事情。当然,倒车只是一个例子。我们希望重用任何转换,只对部分(任何)较大的模板结构进行相同的转换。

2 个答案:

答案 0 :(得分:1)

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

除此之外,您可以为类和int类型的特定模式编写元函数,并且&#34; lift&#34;模板和实例都是关于类(其中intintegral_constant替换),并对它们进行操作(写入&#34; N类型之后的反向类型&#34;以完成相反的操作)。

除此之外,我们可以为您的特定模式提供代码。

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;

可能包含语法错误。计划是将其分为三部分。

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

第二,提取&#34;尾巴&#34;从实例转换为包的参数。

第三,取代&#34;尾巴&#34;与另一包的争论。

连在一起,我们扭转尾巴。对get_tail_t replace_tail_t参数的新模式进行专门化templatereverse_tail_t会使{{1}}&#34;正常工作&#34;。

答案 1 :(得分:-1)

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

template <typename...> struct 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;

假设我们有一些可变参数模板类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的模板参数作为类型列表,RTL是相反的类型列表。要将模板参数作为包提取,我们需要创建类似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, ":(");