C ++中的多模式变量模板 [英] Multi patterend varadic templates in 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屋!