【发布时间】: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 <typename T, size_t N, size_t M> class mat;,它代表一个NxM 矩阵,前面的Vec 相当于广播的列(M),现在我想扩展应用它也可以与 Mat 类一起使用,而不会破坏对 vec 的处理。
规则:
- 如果参数列表包含
NxM矩阵,那么所有矩阵NxM和 所有向量必须是M,函数会返回一个NxM矩阵 - 如果参数列表中的其他规则与之前的向量相同
包含
N向量,则所有向量必须为N,函数将 返回一个N向量
据我所知,由于函数f() 没有为矩阵实现,那么applyH2() 应该以当前形式失败(如果不是,那么它需要),因此应用会失败,因此它应该可以实现另一个apply(),它以相同的方式接受多个大小M和N,如果参数列表包含一个矩阵,则将使用它,然后调用extrV<I,J>(as),对于向量应该映射为@ 987654343@ 这部分接缝非常简单,因为剂量实现dimMatM 和dimMatN 与dimVec 相同,因此dimMatM 映射到dimVec 用于向量。
但是我不确定如何让apply() 上的两个版本一起工作而不会出错。
可选:这部分并不重要,可以说稍后我有一个class tensor<T, N...>,将 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