C++中的多模式瓦拉迪克模板

Multi patterend varadic templates in C++

本文关键字:迪克 模式 C++      更新时间:2023-10-16

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

我有一个向量(数学(课C++

template <typename T, size_t N> class vec;

并希望创建一个瓦拉迪friend函数应用于这些向量

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

这是有效的(尚未测试(

但是我想实现这样的模式

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

请注意,只需要一个参数是向量,任何标量都会被广播到向量的长度。

这个想法是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(V.v[i],tmp.v[i])...}

所以我希望能够写出类似以下内容的东西(C++无效,但它应该给出我想要实现的目标的想法(

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

编辑:

根据弗兰克的评论,我正在寻找类似的东西

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的价值。

不确定你到底想要什么,但是...

在我看来,自定义类型特征可以从类型列表中提取Vec的维度很有用,iff(当且仅当(在类型列表中至少有一个Vec并且没有不同长度的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 };

现在应该很容易。

您可以编写一个apply()函数,该函数接收Args...类型的参数的可变参数列表,并且是否启用了 SFINAEdimVecV<Args...>

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_sequence0传递到N-1到第一个帮助程序函数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...)... }; }

使用从第二个帮助程序函数计算的单个值初始化返回的VecapplyH2()

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

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

它有点长,但不是特别复杂。

以下是完整的工作示例

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

您可以通过部分模板专用化和参数包扩展的组合来实现所需的目标。

#include <array>
template<typename T, std::size_t N>
using Vec = std::array<T, N>;
template<typename T>
struct extract {
static auto exec(T const& v, std::size_t) {return v;}
enum { size = 1 };
};
template<typename T, std::size_t N>
struct extract<Vec<T,N>> {
static auto exec(Vec<T,N> const& v, std::size_t i) {return v[i];}
enum {size = N};
};
template<typename T>
auto extract_v(T const& v, std::size_t i) {return extract<T>::exec(v, i);}
template<typename... args>
struct extract_size {
enum {size = 1};
};
template<typename first, typename... rest>
struct extract_size<first, rest...> {
enum {
rest_size_ = extract_size<rest...>::size,
self_size_ = extract<first>::size,
size = rest_size_ > self_size_ ? rest_size_ : self_size_ 
};
static_assert(self_size_ == 1 || rest_size_ == 1 || rest_size_ == self_size_, "");
};
template<typename F, typename... args_t>
auto apply(F const& cb, args_t&&... args) {
constexpr std::size_t size = extract_size<std::decay_t<args_t>...>::size;
using result_t = decltype(cb(extract_v(std::forward<args_t>(args),0)...));
Vec<result_t, size> result;
for(std::size_t i = 0 ; i < size ; ++i) {
result[i] = cb(extract_v(std::forward<args_t>(args),i)...);
}
return result;
}