【问题标题】:Changing part of a template pack using a pre-written pack transformation使用预先编写的包转换更改模板包的一部分
【发布时间】:2025-11-27 00:35:03
【问题描述】:

假设你有类似的东西

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 &lt;typename, typename, int, typename, int, typename...&gt; 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&lt;P&lt;U,V,M,W,N, Args...&gt;&gt; 派生自Bar&lt;P&lt;U,V,M,W,N, ArgsReversed...&gt;&gt;。如何使用上面定义的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>> {};

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

【问题讨论】:

  • 如果你打算使用元元程序,请使用std::integral_constant 和其他类型的常量包装器。
  • 基于宏的解决方案可以吗?
  • @Yakk。如果宏是确定它的唯一方法。但我认为有办法只使用模板。

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


【解决方案1】:

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

除此之外,您可以为类和 int 类型的特定模式编写元函数,并将模板和实例“提升”为关于类(其中 int 被替换为 integral_constant),然后操作那些(在 N 类型之后写“反向类型”以完成反向操作)。

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

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 个部分。

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

其次,提取“尾部”参数以从实例反转到包中。

第三,用另一个包替换“尾”参数。

钩在一起,我们把尾巴倒过来。作为专门的get_tail_treplace_tail_ttemplate 参数新模式将使reverse_tail_t“正常工作”。

【讨论】:

  • 我还没有为你的代码制作一个完整的例子,但是从一瞥它我们成功地避免了重写Reverse(和RotateRemoveHead等......)对于P 的不同模板签名,但P 的新模板签名将需要为get_tailreplace_tail 编写新的特化,对吧?好吧,至少为每个新模板签名重写新的 2 个特化肯定比重写所有新转换要好。我想没有办法避免必须为任何新的P 重写任何东西,即没有解决方案来处理所有形式的P
  • @prestokeys 如果你的P 缺少非类型模板参数,你可以写一个通用的。您还可以编写代码,将模板的每个模式“提升”为非类型模板,并将模板实例的参数“提升”相同(每个都需要为每个 pattern 自定义代码模板),然后获取您的实例,将其提升为纯类型参数,翻转尾部(从 N 开始),然后将其应用回提升的模板。如果您有许多这样的算法可以使用,这种方法可能会更容易。
  • 然而,真正的解决方案还是在元程序友好的模板代码中,停止使用非类型模板参数。 template&lt;int X&gt;using int_k = typename std::integral_constant&lt;int, X&gt;;,并在那个“插槽”中期望 int_k&lt;N&gt; 而不是 N。最后,请注意,所讨论的P 是模板类型和非类型参数的模式,而不是特定模板。
【解决方案2】:

让我们做一个类型列表:

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&lt;0, 1, ..., N - 1&gt; 的方法:

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作为typelist的模板参数,RTL是同一个typelist颠倒过来。要将模板参数提取为包,我们需要创建类似typelist_get&lt;0, RTL&gt;::type, typelist_get&lt;1, RTL&gt;::type, ..., typelist_get&lt;N - 1, RTL&gt;::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, ":(");

【讨论】:

  • Op 在列表中没有类型...并且想要部分反转。 Op 已经有一个类型列表反向器。
  • 以上我已经完成了。我想要做的是重用上述(或实际上的任何转换 - 反转只是一个示例)以仅对(任何)较大模板结构的一部分进行相同的转换。