我想写一个constexpr函数,它通过二进制操作减少给定的std::array
。即实现
template <typename T, std::size_t N>
reduce(std::array<T, N>, binary_function);
为了简单起见,我想从补充开始。 E.g。
sum(std::array<int, 5>{{1,2,3,4,5}}); // returns 15.
我使用通常的索引技巧来索引数组元素。即生成int
序列,可用于使用参数列表扩展进行索引。
template <int... Is>
struct seq {};
template <int I, int... Is>
struct gen_seq : gen_seq<I - 1, I - 1, Is...> {};
template <int... Is>
struct gen_seq<0, Is...> : seq<Is...> {}; // gen_seq<4> --> seq<0, 1, 2, 3>
然后通过可变参数模板递归定义{{1}}函数。
sum
Here你可以看到它的实际效果。
此实施有效。但是,在这个阶段我确实有几个问题。
// The edge-condition: array of one element.
template <typename T>
constexpr T sum(std::array<T, 1> arr, decltype(gen_seq<0>{})) {
return std::get<0>(arr);
}
// The recursion.
template <typename T, std::size_t N, int... Is>
constexpr auto sum(std::array<T, N> arr, seq<Is...>) -> decltype(T() + T()) {
return sum(std::array<T, N - 1>{ { std::get<Is>(arr)... } },
gen_seq<N - 2>()) +
std::get<N - 1>(arr);
}
// The interface - hides the indexing trick.
template <typename T, std::size_t N>
constexpr auto sum(std::array<T, N> arr)
-> decltype(sum(arr, gen_seq<N - 1>{})) {
return sum(arr, gen_seq<N - 1>{});
}
。即你添加两个元素时得到的结果。虽然在大多数情况下这应该适用于添加,但对于一般的减少可能不再适用。有没有办法获得decltype(T()+T())
的类型?我尝试了类似this的内容,但我不知道如何生成a[0] + (a[1] + (a[2] + ... ) )
的模板参数列表。答案 0 :(得分:2)
我的回答是基于我自己对这些员工的实施。
我更喜欢一般的reduce(或fold或accumulate)函数直接在元素上作为自己的函数参数操作,而不是像std::array
这样的容器。这样,不是在每次递归中构造一个新数组,而是将元素作为参数传递,我想整个操作对于编译器来说更容易内联。而且它更灵活,例如可以直接使用,也可以用在std::tuple
的元素上。一般代码为here。我在这里重复主要功能:
template <typename F>
struct val_fold
{
// base case: one argument
template <typename A>
INLINE constexpr copy <A>
operator()(A&& a) const { return fwd<A>(a); }
// general recursion
template <typename A, typename... An>
INLINE constexpr copy <common <A, An...> >
operator()(A&& a, An&&... an) const
{
return F()(fwd<A>(a), operator()(fwd<An>(an)...));
}
};
我很抱歉这里有很多我自己的定义,所以这里有一些帮助:F
是定义二进制操作的函数对象。 copy
是我对std::decay
的推广,它在数组和元组中进行递归。 fwd
只是std::forward
的快捷方式。类似地,common
只是std::common_type
的快捷方式,但是用于类似的泛化(通常,每个操作可能会产生一个用于延迟评估的表达式模板,这里我们强制进行评估)。
您如何使用上述内容定义sum
?首先定义函数对象
struct sum_fun
{
template <typename A, typename B>
INLINE constexpr copy <common <A, B> >
operator()(A&& a, B&& b) const { return fwd<A>(a) + fwd<B>(b); }
};
然后只是
using val_sum = val_fold<sum_fun>;
从std::array
开始,您会如何称呼它?好吧,一旦你得到了Is...
,你只需要
val_sum()(std::get<Is>(arr)...);
您可以在自己的界面中包装。请注意,在C ++ 14中,std::array::operator[]
是constexpr,所以这只是
val_sum()(arr[Is]...);
现在,问题:
1)转发:是的,std::get
将数组元素转发到val_sum
,它以递归方式将所有内容转发给自身。所以剩下的就是你自己的转发输入数组的接口,例如
template <typename A, /* enable_if to only allow arrays here */>
constexpr auto sum(A&& a) -> /* return type here */
{
return sum(std::forward<A>(a), gen_seq_array<A>{});
}
依此类推,其中gen_seq_array
将采用原始类型A(std::remove_ref
,std::remove_cv
等),推断N
,并致电gen_seq<N>{}
。如果数组元素具有移动语义,则转发是有意义的。它应该无处不在,例如上面val_sum
的调用类似于
val_sum()(std::get<Is>(std::forward<A>(a))...);
2)返回类型:如您所见,我使用std::common_type
作为返回类型,这应该用于sum
和最常见的算术运算。这已经是可变的。如果您想要自己的类型函数,可以使用模板递归轻松地从二进制类型函数中生成可变参数。
我自己的common
版本为here。它涉及的更多,但它仍然是一个递归模板,包含一些decltype
来完成实际工作。
在任何情况下,这都比您需要的更通用,因为它是针对任意给定类型的任意数量定义的。
,你的数组中只有一种类型T
,所以你拥有的就足够了。