C ++中的多模式变量模板 [英] Multi patterend varadic templates in C++

查看:54
本文介绍了C ++中的多模式变量模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据我所读的内容,我认为这是不可能的,但是我希望这里的人可能知道一些解决方案,可以使它起作用。

I don't think this is possible based on what I've read however I'm hoping someone here may know of some solution that would get this to work.

我有一个C ++的向量(数学)类

I have a vector (maths) class for C++

template <typename T, size_t N> class vec;

并想创建一个变幻的 friend 函数

And want to create a varadic friend function apply to apply a function to these vectors element-wise

ie

template <typename F, typename ...Args> friend vec<typename std::result_of<pow(Args&&...)>::type, N> apply(F&& f, const vec<Args, N>&... args);

这是有效的(尚未试用)

which is valid (untested yet)

但是我想实现这样的模式

however I want to achieve a pattern like

template <typename F> friend vec<typename std::result_of<F&&(T&&)>::type, N> apply(F&& f, const vec<T, N>& V);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const vec<T, N>& V2);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const T& V2);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const T& V1, const vec<T, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(T&&, U&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const vec<U, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(T&&, U&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const U& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(U&&, T&&)>::type, N> apply(F&& f, const vec<U, N>& V1, const vec<T, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(U&&, T&&)>::type, N> apply(F&& f, const U& V1, const vec<T, N>& V2);

请注意,只有一个自变量必须是向量,任何标量都将广播到一定长度

note that only one of the arguments is required to be a vector any scalars would be broadcasted to the length of the vector.

这个想法是 apply(pow,/ * vec< float,N> * / V,/ * int * / n)-> {pow(V.v [i],n)...} 其中 i-> 0 ... N 而不是 apply(pow,/ * vec< float,N> * / V,/ * int * / n)-> apply(pow,/ * vec< float,N> * / V,/ * vec< int,N> * / tmp {/ * int * / n}){pow(Vv [i],tmp.v [i] )...}

The idea is that apply(pow, /*vec<float,N>*/V, /*int*/n) -> {pow(V.v[i],n)...} where i -> 0 ... N rather than apply(pow, /*vec<float,N>*/V, /*int*/n) -> apply(pow, /*vec<float,N>*/V, /*vec<int,N>*/tmp{/*int*/n}) {pow(V.v[i],tmp.v[i])...}

因此,我希望能够编写类似以下内容(不是有效的C ++,但它应该给出我想要实现的目标)

So I would like to be able to write something like the following (which isn't valid C++, but it should give an idea of what I want to achieve)

template <typename F, typename ...Args> friend vec<typename std::result_of<pow(Args&&...)>::type, N> apply(F&& f, const vec<Args, N>&||scalar<Args>::type... args) {
    vec<typename std::result_of<pow(Args&&...)>::type, N> r;
    for (int i= 0; i < N; i++) { r = f((is_vec<Args>?args.v[i]:args)...); }
    return r;
}

编辑:

根据弗兰克的意见,我正在寻找类似

Based on Frank's comments I'm looking for something along the lines of

template<typename F, typename ...Args, size_t N>
vec<typename std::enable_if<sum<is_vec<Args,N>...>::value > 0, std::result_of<F&&(base_type<Args>::type&&...)>::type>::type, N>
(F&& f, Args&&...args) {
    vec<typename std::result_of<F&&(base_type<Args>::type&&...)>::type, N> result;
    for(std::size_t i = 0 ; i < N ; ++i) { result.v[i] = f(extract_v(std::forward<Args>(args),i)...); }
    return result;
}

但是我不确定该版本是否可以编译,因为它可能也是如此模棱两可,以至于不会损害 N 的值。

however I'm unsure if this version could even compile as it may be too ambiguous to be able to detriment the value of N.

推荐答案

不一定要了解您到底想要什么,但是...

Not sure to understand what do you exactly want but...

在我看来,自定义类型特征可以从类型列表中提取维可能很有用 Vec 中的一个,如果(如果且仅当)类型列表中,至少有一个 Vec 且不是 Vec 的长度不同。

It seems to me that can be useful a custom type traits to extract, from a list of types, the dimension of the Vec, iff (if and only if) in the list of types there is at least one Vec and there aren't Vec's of different lengths.

我主要基于模板专业化,提出以下建议,

I suggest something as follows, heavily based on template specialization,

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 };

现在应该很容易。

您可以编写一个 apply()函数,该函数接收变量类型为 Args ... 的可变列表,并启用SFINAE iff dimVecV< Args ...> 已定义

You can write an apply() function that receive a variadic list of args of types Args... and is SFINAE enabled iff dimVecV<Args...> is defined

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...); }

请注意使用 N 变量SFINAE启用/禁用该功能,但它本身很有用:它用于将 std :: index_sequence 0 传递给 N-1 到第一个帮助函数 applyH1()

Observe that the N variable is used to SFINAE enable/disable the function but is useful itself: it's used to pass a std::index_sequence from 0 to N-1 to the first helper function applyH1()

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...)... }; }

可以使用单个初始化返回的 Vec 从第二个辅助函数计算的值 applyH2()

that initialize the returned Vec with single values calculated from the second helper function applyH2()

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

使用一组模板函数 extrV()

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; }

提取第 I 个元素从 Vec 或传递标量值。

to extract the I-th element from a Vec or to pass-through a scalar value.

虽然有点长,但并不特别复杂。

It's a little long but not particularly complicated.

以下是完整的示例

#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

 }

这篇关于C ++中的多模式变量模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆