【问题标题】:Extending Multi patterned variadic templates in C++在 C++ 中扩展多模式可变参数模板
【发布时间】:2018-05-14 01:55:46
【问题描述】:

这个问题是我上一个问题Multi patterned varadic templates in C++ 的后续问题,我收到了解决方案:

#include <array>
#include <iostream>
#include <type_traits>

template <typename T, std::size_t N>
class Vec;

template <std::size_t, typename ...>
struct dimVec;

// ground case for no Vecs: unimplemented for SFINAE failure !
template <>
struct dimVec<0U>;

// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
 { };

// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
 { };

// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
 { };

// another Vec of different size: unimplemented for SFINAE failure !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;

// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
 { };

template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };

template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
 { return v[I]; }

template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
 { return v; }

template <typename T, std::size_t N>
class Vec
 {
   private:
      std::array<T, N> d;

   public:
      template <typename ... Ts>
      Vec (Ts ... ts) : d{{ ts... }}
       { }

      T & operator[] (int i)
       { return d[i]; }

      T const & operator[] (int i) const
       { return d[i]; }
 };


template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
 { return f(extrV<I>(as)...); }

template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
   -> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
 { return { applyH2<Is>(f, as...)... }; }

template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
 { return applyH1(std::make_index_sequence<N>{}, f, as...); }

long foo (int a, int b)
 { return a + b + 42; }

int main ()
 {
   Vec<int, 3U>  v3;
   Vec<int, 2U>  v2;

   auto r1 { apply(foo, v2, v2) };
   auto r2 { apply(foo, v3, v3) };
   auto r3 { apply(foo, v3, 0)  };

   static_assert( std::is_same<decltype(r1), Vec<long, 2U>>{}, "!" );
   static_assert( std::is_same<decltype(r2), Vec<long, 3U>>{}, "!" );
   static_assert( std::is_same<decltype(r3), Vec<long, 3U>>{}, "!" );

   // apply(foo, v2, v3); // compilation error
   // apply(foo, 1, 2);   // compilation error

 }

现在假设我还有一个模板类template &lt;typename T, size_t N, size_t M&gt; class mat;,它代表一个NxM 矩阵,前面的Vec 相当于广播的列(M),现在我想扩展应用它也可以与 Mat 类一起使用,而不会破坏对 vec 的处理。

规则:

  • 如果参数列表包含NxM 矩阵,那么所有矩阵NxM 和 所有向量必须是M,函数会返回一个NxM矩阵
  • 如果参数列表中的其他规则与之前的向量相同 包含N 向量,则所有向量必须为N,函数将 返回一个N 向量

据我所知,由于函数f() 没有为矩阵实现,那么applyH2() 应该以当前形式失败(如果不是,那么它需要),因此应用会失败,因此它应该可以实现另一个apply(),它以相同的方式接受多个大小MN,如果参数列表包含一个矩阵,则将使用它,然后调用extrV&lt;I,J&gt;(as),对于向量应该映射为@ 987654343@ 这部分接缝非常简单,因为剂量实现dimMatMdimMatNdimVec 相同,因此dimMatM 映射到dimVec 用于向量。

但是我不确定如何让apply() 上的两个版本一起工作而不会出错。

可选:这部分并不重要,可以说稍后我有一个class tensor&lt;T, N...&gt;,将 vec 和 mat 的概念扩展到 N 维,是否可以将 apply 扩展到工作,以便广播发生在主要维度即

double f(float x, float y);
int main() {
   tensor<float, 2, 4, 6> f246;
   tensor<int, 4, 6> i46;
   tensor<float, 2, 4> f24;

   auto r1 = apply(f, f246, i46); // returns tensor<double, 2, 4, 6>
   // apply(f, f24, f246); // Compile ERROR: dimensions don't match
}

编辑:

我只是想出了一个可能的解决方案,或者至少部分解决方案见下文

template <size_t ...N>
struct tensor_helper {
template <size_t I, typename T, size_t ...M> static tensorGet(const tensor<T, M...>& V) { return V.v[I % product<M...>::value]; }
template <size_t I, typename T> static tensorGet(const T& V) { return V; }
template <std::size_t I, typename F, typename ...Args>
static auto applyH2 (F && f, Args ...as) -> decltype(f(std::declval<typename base_type<Args>::type>()...))
 { return f(tensorGet<I>(as)...); }
template <size_t ... Is, typename F, typename ... Args>
static auto applyH1(std::index_sequence<Is...> const &..., F && f, Args ... as) -> tensor<decltype(f(std::declval<typename base_type<Args>::type>()...)), N...>
    { return make_tensor<decltype(f(std::declval<typename base_type<Args>::type>()...)), N...>({applyH2<Is>(f, as...)... }); }
}

template <typename F, typename ...Args, size_t ...N = tensorSize<Args...> /* How do I get a list here? */>
auto apply (F && f, Args ... as)
 { return tensor_helper<N...>::applyH1(std::make_index_sequence<product<N...>>{}..., f, as...); }

唯一的问题是如何让tensorSize() 提供一个维度列表,尽管我认为如果只是为向量和矩阵实现这可以手动扩展,而不是使用维度的参数包。

编辑 2: 我相信我可以使用constexpr array to variadic template,从上面的代码中解决N 列表问题。但我也太尝试了,今晚尝试实现这个概念,明天就会尝试。

【问题讨论】:

  • 你能告诉我们你尝试了什么,以及它是如何失败的吗?无意冒犯,但听起来你只是想让我们为你写这篇文章。
  • @Frank,我没有问题将助手扩展到矩阵我解释了我将如何处理我的问题,我的问题是我有两个版本的 apply 函数,原始版本只需要向量和标量并返回一个向量,以及一个接受矩阵、向量和标量并返回矩阵的新向量。我不知道如何让编译器选择正确的版本,即它必须选择与最高维度参数匹配的版本,或者如果参数是矩阵,则选择矩阵版本,否则选择向量版本。
  • 据我所知,他们的调用签名是相同的,只是接受一个额外的维度,我缺乏熟悉的模板元编程来确保编译器选择正确的版本。

标签: c++ templates c++14 variadic-templates template-meta-programming


【解决方案1】:

在概念上非常相似,但在两个维度上,简直就是一场噩梦。

我建议使用二维(NM)而不是 dimVec,并针对不可接受的情况(不同尺寸的 vecs 或垫子,尺寸与垫子不兼容的尺寸)进行 SFINAE 失败;不确定所有案例都得到管理,但应该是

template <std::size_t, std::size_t, typename ...>
struct dimMat;

// ground case for no Vecs and no Mats: unimplemented for SFINAE failure
template <>
struct dimMat<0U, 0U>;

// ground case with one or more Vecs and/or Mats: sizes fixed
template <std::size_t N, std::size_t M>
struct dimMat<N, M>
 { 
   static constexpr std::size_t valN { N };
   static constexpr std::size_t valM { M };
 };

// first Vec: M detected
template <std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, 0U, Vec<T, M>, Ts...> : public dimMat<0U, M, Ts...>
 { };

// first Mat: N and M detected
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, 0U, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
 { };

// first Mat after a correct Vect: N detected
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, M, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
 { };

// another Vec of correct size: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, Vec<T, M>, Ts...> : public dimMat<N, M, Ts...>
 { };

// another Mat of correct sizes: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
 { };

// another Vec of different size: unimplemented for SFINAE failure
template <std::size_t N, std::size_t M1, std::size_t M2, typename T,
          typename ... Ts>
struct dimMat<N, M1, Vec<T, M2>, Ts...>;

// another Mat of different sizes: unimplemented for SFINAE failure
template <std::size_t N1, std::size_t N2, std::size_t M1, std::size_t M2,
          typename T, typename ... Ts>
struct dimMat<N1, M1, Mat<T, N2, M2>, Ts...>;

// a not-Vec, not-Mat type: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, T, Ts...> : public dimMat<N, M, Ts...>
 { };

辅助模板变量很简单

template <typename ... Args>
static constexpr auto dimMatN { dimMat<0U, 0U, Args...>::valN };

template <typename ... Args>
static constexpr auto dimMatM { dimMat<0U, 0U, Args...>::valM };

对于apply(),改名为applyM(),我想应该是

template <typename F, typename ... Args, std::size_t N = dimMatN<Args...>,
          std::size_t M = dimMatM<Args...>>
auto applyM (F && f, Args ... as)
 { return applyMH1(std::make_index_sequence<N>{},
                   std::make_index_sequence<M>{}, 
                   f, as...); }

对于applyMH1() 两种情况:如果N 为零,所以第一个参数是空索引列表,我们有 vec 情况

// Vec case: the first index list is empty: call applyH2()
template <std::size_t ... Js, typename F, typename ... Args>
auto applyMH1 (std::index_sequence<> const &,
               std::index_sequence<Js...> const &, F && f, Args ... as)
   -> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Js)>
 { return { applyH2<Js>(f, as...)... }; }

否则垫套

template <std::size_t ... Is, std::size_t ... Js, typename F,
          typename ... Args>
auto applyMH1 (std::index_sequence<Is...> const &,
               std::index_sequence<Js...> const & js, F && f, Args ... as)
   -> Mat<decltype(applyMH3<0U, 0U>(f, as...)), sizeof...(Is), sizeof...(Js)>
 { return {{{ applyMH2<Is>(js, f, as...) ... }}}; }

现在applyMH2()

template <std::size_t I, std::size_t ... Js, typename F, typename ... Args>
auto applyMH2 (std::index_sequence<Js...> const &, F && f, Args ... as)
   -> std::array<decltype(applyMH3<0U, 0U>(f, as...)), sizeof...(Js)>
 { return {{ applyMH3<I, Js>(f, as...)... }}; }

applyMH3()

template <std::size_t I, std::size_t J, typename F, typename ... Args>
auto applyMH3 (F && f, Args ... as)
 { return f(extrM<I, J>(as)...); }

基于extrV() 给出extrM()

template <std::size_t I, std::size_t J, typename T,
          std::size_t N, std::size_t M>
constexpr auto extrM (Mat<T, N, M> const & v)
 { return v[I][J]; }

template <std::size_t I, std::size_t J, typename T>
constexpr auto extrM (T const & v)
 { return extrV<J>(v); }

以下是完整的编译示例

#include <array>
#include <iostream>
#include <type_traits>

template <typename T, std::size_t N>
class Vec;

template <typename T, std::size_t N, std::size_t M>
struct Mat;

template <std::size_t, std::size_t, typename ...>
struct dimMat;

// ground case for no Vecs and no Mats: unimplemented for SFINAE failure
template <>
struct dimMat<0U, 0U>;

// ground case with one or more Vecs and/or Mats: sizes fixed
template <std::size_t N, std::size_t M>
struct dimMat<N, M>
 { 
   static constexpr std::size_t valN { N };
   static constexpr std::size_t valM { M };
 };

// first Vec: M detected
template <std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, 0U, Vec<T, M>, Ts...> : public dimMat<0U, M, Ts...>
 { };

// first Mat: N and M detected
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, 0U, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
 { };

// first Mat after a correct Vect: N detected
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, M, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
 { };

// another Vec of correct size: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, Vec<T, M>, Ts...> : public dimMat<N, M, Ts...>
 { };

// another Mat of correct sizes: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
 { };

// another Vec of different size: unimplemented for SFINAE failure
template <std::size_t N, std::size_t M1, std::size_t M2, typename T,
          typename ... Ts>
struct dimMat<N, M1, Vec<T, M2>, Ts...>;

// another Mat of different sizes: unimplemented for SFINAE failure
template <std::size_t N1, std::size_t N2, std::size_t M1, std::size_t M2,
          typename T, typename ... Ts>
struct dimMat<N1, M1, Mat<T, N2, M2>, Ts...>;

// a not-Vec, not-Mat type: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, T, Ts...> : public dimMat<N, M, Ts...>
 { };

template <typename ... Args>
static constexpr auto dimMatN { dimMat<0U, 0U, Args...>::valN };

template <typename ... Args>
static constexpr auto dimMatM { dimMat<0U, 0U, Args...>::valM };

template <std::size_t, typename ...>
struct dimVec;

// ground case for no Vecs: unimplemented !
template <>
struct dimVec<0U>;

// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
 { };

// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
 { };

// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
 { };

// another Vec of different size: unimplemented !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;

// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
 { };

template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };

template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
 { return v[I]; }

template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
 { return v; }

template <std::size_t I, typename T, std::size_t N, std::size_t M>
constexpr auto extrV (Mat<T, N, M> const & v)
 { return 0; }

template <std::size_t I, std::size_t J, typename T,
          std::size_t N, std::size_t M>
constexpr auto extrM (Mat<T, N, M> const & v)
 { return v[I][J]; }

template <std::size_t I, std::size_t J, typename T>
constexpr auto extrM (T const & v)
 { return extrV<J>(v); }


template <typename T, std::size_t N>
class Vec
 {
   private:
      std::array<T, N> d;

   public:
      template <typename ... Ts>
      Vec (Ts ... ts) : d{{ ts... }}
       { }

      T & operator[] (int i)
       { return d[i]; }

      T const & operator[] (int i) const
       { return d[i]; }
 };

template <typename T, std::size_t N, std::size_t M>
struct Mat
 {
   std::array<std::array<T, M>, N> m;

   auto & operator[] (int i)
    { return m[i]; }

   auto const & operator[] (int i) const
    { return m[i]; }
 };


template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
 { return f(extrV<I>(as)...); }

template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
   -> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
 { return { applyH2<Is>(f, as...)... }; }

template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
 { return applyH1(std::make_index_sequence<N>{}, f, as...); }

template <std::size_t I, std::size_t J, typename F, typename ... Args>
auto applyMH3 (F && f, Args ... as)
 { return f(extrM<I, J>(as)...); }

template <std::size_t I, std::size_t ... Js, typename F, typename ... Args>
auto applyMH2 (std::index_sequence<Js...> const &, F && f, Args ... as)
   -> std::array<decltype(applyMH3<0U, 0U>(f, as...)), sizeof...(Js)>
 { return {{ applyMH3<I, Js>(f, as...)... }}; }

// Vec case: the first index list is empty: call applyH2()
template <std::size_t ... Js, typename F, typename ... Args>
auto applyMH1 (std::index_sequence<> const &,
               std::index_sequence<Js...> const &, F && f, Args ... as)
   -> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Js)>
 { return { applyH2<Js>(f, as...)... }; }

template <std::size_t ... Is, std::size_t ... Js, typename F,
          typename ... Args>
auto applyMH1 (std::index_sequence<Is...> const &,
               std::index_sequence<Js...> const & js, F && f, Args ... as)
   -> Mat<decltype(applyMH3<0U, 0U>(f, as...)), sizeof...(Is), sizeof...(Js)>
 { return {{{ applyMH2<Is>(js, f, as...) ... }}}; }

template <typename F, typename ... Args, std::size_t N = dimMatN<Args...>,
          std::size_t M = dimMatM<Args...>>
auto applyM (F && f, Args ... as)
 { return applyMH1(std::make_index_sequence<N>{},
                   std::make_index_sequence<M>{}, 
                   f, as...); }

long foo (int a, int b)
 { return a + b + 42; }

int main ()
 {
   Vec<int, 3U>      v3;
   Vec<int, 2U>      v2;
   Mat<int, 2U, 3U>  m23;
   Mat<int, 2U, 4U>  m24;

   auto r1 { applyM(foo, v2, v2) };
   auto r2 { applyM(foo, v3, v3) };
   auto r3 { applyM(foo, v3, 0)  };
   auto r4 { applyM(foo, v3, m23) };
   auto r5 { applyM(foo, m24, 0) };

   static_assert( std::is_same<decltype(r1), Vec<long, 2U>>{}, "!" );
   static_assert( std::is_same<decltype(r2), Vec<long, 3U>>{}, "!" );
   static_assert( std::is_same<decltype(r3), Vec<long, 3U>>{}, "!" );
   static_assert( std::is_same<decltype(r4), Mat<long, 2U, 3U>>{}, "!" );
   static_assert( std::is_same<decltype(r5), Mat<long, 2U, 4U>>{}, "!" );

   //applyM(foo, v2, v3);   // compilation error
   //applyM(foo, 1, 2);     // compilation error
   //applyM(foo, v2, m23);  // compilation error
   //applyM(foo, m24, m23); // compilation error
 }

【讨论】:

  • 我可以从您的原始答案中推断出大部分内容,我在这里真正想要的是一种重载应用的方法,因此它适用于 mat 或 vec。没有将 apply 和 applyM 作为单独的函数,因为这只是添加了一个额外的维度。我不知道该怎么做是确保编译器将选择正确的应用版本。
  • 我的示例背后的想法是,至少包含一个 MatArgs... 列表应该遵循更通用的路径 (applyM() -> applyMH1() -> applyMH2() -> applyMH3(),返回一个Mat),其中Args... 的列表不包含Mats,但只有Vecs 和标量,应该自动遵循简化的Vecs 路径(applyM() -> @ 987654354@(第一个列表为空的版本)-> applyH2(),返回Vec)。在这一刻我没有时间,但我会在几个小时内修改我的示例以使其更清晰。
  • @glenflet - 答案改进:如您所见,applyM() 替换旧的apply()
  • @glenflet - 如您所见,这个解决方案是一场噩梦;也许可变参数tensor&lt;&gt; 方式会更有趣。也许第二天我会尝试一些东西。
【解决方案2】:

如果您仍然对基于可变参数 N 维 tensor&lt;&gt; 的解决方案感兴趣...嗯...这是一场噩梦,但却是一场不同类型的噩梦。

首先,我为std::array准备了以下递归包装器

template <typename T, std::size_t ...>
struct tensor;

template <typename T, std::size_t N, std::size_t ... Ns>
struct tensor<T, N, Ns...>
 { 
   using nextT = std::conditional_t<(sizeof...(Ns) > 0U),
                    tensor<T, Ns...>, T>;

   std::array<nextT, N>  value;

   auto const & operator[] (std::size_t i) const
    { return value[i]; }

   auto & operator[] (std::size_t i)
    { return value[i]; }
 };

extrV() 函数现在是递归的,并接受 std::index_sequence 中的索引列表

// scalar case: return value
template <std::size_t ... Is, typename T>
constexpr auto extrV (std::index_sequence<Is...> const &, T const & t)
 { return t; }

// tensor case with lower dimension: skip the first requested index 
template <std::size_t I0, std::size_t ... Is,
          typename T, std::size_t ... Js>
constexpr auto extrV
   (std::index_sequence<I0, Is...> const &, tensor<T, Js...> const & t,
    std::enable_if_t<(sizeof...(Is) >= sizeof...(Js))> * = nullptr)
 { return extrV(std::index_sequence<Is...>{}, t); }

// tensor case with exact dimension: use the first requested index 
template <std::size_t I0, std::size_t ... Is,
          typename T, std::size_t ... Js>
constexpr auto extrV
   (std::index_sequence<I0, Is...> const &, tensor<T, Js...> const & t,
    std::enable_if_t<(sizeof...(Is)+1U == sizeof...(Js))> * = nullptr)
 { return extrV(std::index_sequence<Is...>{}, t[I0]); }

现在自定义类型特征,给定 Args... 类型的可变参数列表,提取最长的公共索引序列(如果有)并将其返回到 std::index_sequence 中(以防万一)

template <typename ...>
struct commonDims;

// ground case: define type as surviving parameter
template <std::size_t I0, std::size_t ... Is>
struct commonDims<std::index_sequence<I0, Is...>>
 { using type = std::index_sequence<I0, Is...>; };

// no tensor type: continue
template <typename IS, typename T0, typename ... Ts>
struct commonDims<IS, T0, Ts...> : public commonDims<IS, Ts...>
 { };

// tensor type: continue with bigger common index list (if any)
template <std::size_t ... Is, typename T, std::size_t ... Js,
          typename ... Ts>
struct commonDims<std::index_sequence<Is...>, tensor<T, Js...>, Ts...>
   : public commonDims<greaterSeqT<std::index_sequence<Is...>,
                                   std::index_sequence<Js...>,
                                   (sizeof...(Is) > sizeof...(Js))>, Ts...>
 { };

template <typename ... Ts>
using commonDimsT = typename commonDims<std::index_sequence<>, Ts...>::type;

如您所见,它使用 greaterSeq 辅助类型特征

template <typename, typename, bool>
struct greaterSeq;

template <typename T1, typename T2>
struct greaterSeq<T1, T2, true> : public gsHelper<T1, T1, T2>
 { };

template <typename T1, typename T2>
struct greaterSeq<T1, T2, false> : public gsHelper<T2, T2, T1>
 { };

template <typename T1, typename T2, bool B>
using greaterSeqT = typename greaterSeq<T1, T2, B>::type;

使用另一个 gsHelper 辅助类型特征

template <typename, typename, typename, typename = std::true_type>
struct gsHelper;

// sequences of different lengths: skipp the first index in longest
template <typename T, std::size_t I0, std::size_t ... Is, std::size_t ... Js>
struct gsHelper<T, std::index_sequence<I0, Is...>,
          std::index_sequence<Js...>,
          std::integral_constant<bool, (sizeof...(Is) >= sizeof...(Js))>>
   : public gsHelper<T, std::index_sequence<Is...>,
                     std::index_sequence<Js...>>
 { };

template <typename T, typename IS>
struct gsHelper<T, IS, IS>
 { using type = T; };

现在是apply() 函数

template <typename F, typename ... Args, typename IS = commonDimsT<Args...>>
auto apply (F && f, Args const & ... as)
 { return applyH1(std::index_sequence<>{}, IS{}, f, as...); }

applyH1()applyH2() 函数之间开始递归调用

template <std::size_t ... Is, std::size_t ... Js, std::size_t ... Ks,
          typename F, typename ... Args>
auto applyH2 (std::index_sequence<Is...> const &,
              std::index_sequence<Js...> const &,
              std::index_sequence<Ks...> const & ks,
              F && f, Args const & ... as)
   -> tensor<
         decltype(call(std::index_sequence<(Is, 0U)..., 0U, (Ks, 0U)...>{},
                       f, as...)), sizeof...(Js), Ks...>
 { return {{{ applyH1(std::index_sequence<Is..., Js>{},
                      ks, f, as...) ... }}}; }

template <typename IS, typename F, typename ... Args>
auto applyH1 (IS const & is, std::index_sequence<> const &,
              F && f, Args const & ... as)
 { return call(is, f, as...); }

template <typename IS, std::size_t J0, std::size_t ... Js,
          typename F, typename ... Args>
auto applyH1 (IS const & is, std::index_sequence<J0, Js...> const &,
              F && f, Args const & ... as)
 { return applyH2(is, std::make_index_sequence<J0>{},
                  std::index_sequence<Js...>{}, f, as...); }

通过调用call()而终止

template <typename IS, typename F, typename ... Args>
auto call (IS const & is, F && f, Args const & ... as)
 { return f(extrV(is, as)...); }

以下是完整的编译示例

#include <array>
#include <string>
#include <iostream>

template <typename T, std::size_t ...>
struct tensor;

template <typename T, std::size_t N, std::size_t ... Ns>
struct tensor<T, N, Ns...>
 { 
   using nextT = std::conditional_t<(sizeof...(Ns) > 0U),
                    tensor<T, Ns...>, T>;

   std::array<nextT, N>  value;

   auto const & operator[] (std::size_t i) const
    { return value[i]; }

   auto & operator[] (std::size_t i)
    { return value[i]; }
 };

// scalar case: return value
template <std::size_t ... Is, typename T>
constexpr auto extrV (std::index_sequence<Is...> const &, T const & t)
 { return t; }

// tensor case with lower dimension: skip the first requested index 
template <std::size_t I0, std::size_t ... Is,
          typename T, std::size_t ... Js>
constexpr auto extrV
   (std::index_sequence<I0, Is...> const &, tensor<T, Js...> const & t,
    std::enable_if_t<(sizeof...(Is) >= sizeof...(Js))> * = nullptr)
 { return extrV(std::index_sequence<Is...>{}, t); }

// tensor case with exact dimension: use the first requested index 
template <std::size_t I0, std::size_t ... Is,
          typename T, std::size_t ... Js>
constexpr auto extrV
   (std::index_sequence<I0, Is...> const &, tensor<T, Js...> const & t,
    std::enable_if_t<(sizeof...(Is)+1U == sizeof...(Js))> * = nullptr)
 { return extrV(std::index_sequence<Is...>{}, t[I0]); }

template <typename, typename, typename, typename = std::true_type>
struct gsHelper;

// sequences of different lengths: skipp the first index in longest
template <typename T, std::size_t I0, std::size_t ... Is, std::size_t ... Js>
struct gsHelper<T, std::index_sequence<I0, Is...>,
          std::index_sequence<Js...>,
          std::integral_constant<bool, (sizeof...(Is) >= sizeof...(Js))>>
   : public gsHelper<T, std::index_sequence<Is...>,
                     std::index_sequence<Js...>>
 { };

template <typename T, typename IS>
struct gsHelper<T, IS, IS>
 { using type = T; };

template <typename, typename, bool>
struct greaterSeq;

template <typename T1, typename T2>
struct greaterSeq<T1, T2, true> : public gsHelper<T1, T1, T2>
 { };

template <typename T1, typename T2>
struct greaterSeq<T1, T2, false> : public gsHelper<T2, T2, T1>
 { };

template <typename T1, typename T2, bool B>
using greaterSeqT = typename greaterSeq<T1, T2, B>::type;

template <typename ...>
struct commonDims;

// ground case: define type as surviving parameter
template <std::size_t I0, std::size_t ... Is>
struct commonDims<std::index_sequence<I0, Is...>>
 { using type = std::index_sequence<I0, Is...>; };

// no tensor type: continue
template <typename IS, typename T0, typename ... Ts>
struct commonDims<IS, T0, Ts...> : public commonDims<IS, Ts...>
 { };

// tensor type: continue with bigger common index list (if any)
template <std::size_t ... Is, typename T, std::size_t ... Js,
          typename ... Ts>
struct commonDims<std::index_sequence<Is...>, tensor<T, Js...>, Ts...>
   : public commonDims<greaterSeqT<std::index_sequence<Is...>,
                                   std::index_sequence<Js...>,
                                   (sizeof...(Is) > sizeof...(Js))>, Ts...>
 { };

template <typename ... Ts>
using commonDimsT = typename commonDims<std::index_sequence<>, Ts...>::type;

template <typename IS, typename F, typename ... Args>
auto call (IS const & is, F && f, Args const & ... as)
 { return f(extrV(is, as)...); }

template <std::size_t ... Is, std::size_t ... Js, std::size_t ... Ks,
          typename F, typename ... Args>
auto applyH2 (std::index_sequence<Is...> const &,
              std::index_sequence<Js...> const &,
              std::index_sequence<Ks...> const & ks,
              F && f, Args const & ... as)
   -> tensor<
         decltype(call(std::index_sequence<(Is, 0U)..., 0U, (Ks, 0U)...>{},
                       f, as...)), sizeof...(Js), Ks...>
 { return {{{ applyH1(std::index_sequence<Is..., Js>{},
                      ks, f, as...) ... }}}; }

template <typename IS, typename F, typename ... Args>
auto applyH1 (IS const & is, std::index_sequence<> const &,
              F && f, Args const & ... as)
 { return call(is, f, as...); }

template <typename IS, std::size_t J0, std::size_t ... Js,
          typename F, typename ... Args>
auto applyH1 (IS const & is, std::index_sequence<J0, Js...> const &,
              F && f, Args const & ... as)
 { return applyH2(is, std::make_index_sequence<J0>{},
                  std::index_sequence<Js...>{}, f, as...); }

template <typename F, typename ... Args, typename IS = commonDimsT<Args...>>
auto apply (F && f, Args const & ... as)
 { return applyH1(std::index_sequence<>{}, IS{}, f, as...); }

long foo (int a, int b)
 { return a + b + 42; }

int main ()
 { 
   tensor<int, 2, 3, 4, 5>  t0;

   t0[0][0][0][0] = 1;

   using t1 = commonDimsT<tensor<int, 1>, long, tensor<long, 3, 2, 1>, int>;

   static_assert(std::is_same<t1, std::index_sequence<3, 2, 1>>{}, "!");

   auto r1 { apply(foo, tensor<int, 3, 2, 1>{}, 0) };
   auto r2 { apply(foo, tensor<int, 3, 2, 1>{}, tensor<int, 3, 2, 1>{}) };
   auto r3 { apply(foo, tensor<int, 3, 2, 1>{}, tensor<int, 2, 1>{}) };
   auto r4 { apply(foo, tensor<int, 3, 2, 1>{}, tensor<int, 1>{}) };
   auto r5 { apply(foo, 0, tensor<int, 1>{}) };

   static_assert(std::is_same<decltype(r1), tensor<long, 3, 2, 1>>{}, "!");
   static_assert(std::is_same<decltype(r2), tensor<long, 3, 2, 1>>{}, "!");
   static_assert(std::is_same<decltype(r3), tensor<long, 3, 2, 1>>{}, "!");
   static_assert(std::is_same<decltype(r4), tensor<long, 3, 2, 1>>{}, "!");
   static_assert(std::is_same<decltype(r5), tensor<long, 1>>{}, "!");

   // compilation errors (no common tensor)
   //apply(foo, 0, 0);
   //apply(foo, tensor<int, 3, 2, 1>{}, tensor<int, 2>{});
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    • 2014-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    相关资源
    最近更新 更多