我想写一个constexpr函数,它减少std::array了二进制操作的给定.即实现的功能
template <typename T, std::size_t N>
reduce(std::array<T, N>, binary_function);
Run Code Online (Sandbox Code Playgroud)
为了简单起见,我想从补充开始.例如
sum(std::array<int, 5>{{1,2,3,4,5}}); // returns 15.
Run Code Online (Sandbox Code Playgroud)
我使用通常的索引技巧来索引数组元素.即生成一个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>
Run Code Online (Sandbox Code Playgroud)
sum然后通过可变参数模板递归定义该函数.
// 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>{});
}
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到它的实际效果.
这种实现有效.但是,在这个阶段我确实有几个问题.
decltype(T()+T()).也就是你添加两个元素时得到的东西.虽然在大多数情况下这应该适用于添加,但对于一般的减少可能不再适用.有没有办法获得这种类型的a[0] + (a[1] + (a[2] + ... ) )?我试着像这样,但我不知道我怎么能产生的模板参数列表<T, T, T, ...>.我的回答是基于我自己对此类人员的实施。
我更喜欢一般的reduce(或fold,或accumulate)函数作为其自己的函数参数直接对元素进行操作,而不是在像std::array. 这样,元素将作为参数传递,而不是在每次递归中构造一个新数组,我想整个操作对于编译器来说更容易内联。另外它更灵活,例如可以直接使用或在std::tuple. 通用代码在这里。我在这里重复一下主要功能:
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)...));
}
};
Run Code Online (Sandbox Code Playgroud)
很抱歉,这充满了我自己的定义,所以这里有一些帮助: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); }
};
Run Code Online (Sandbox Code Playgroud)
然后就
using val_sum = val_fold<sum_fun>;
Run Code Online (Sandbox Code Playgroud)
当以 a 开头时,你会如何称呼它std::array?好吧,一旦你有了你的Is...,你所需要的就是
val_sum()(std::get<Is>(arr)...);
Run Code Online (Sandbox Code Playgroud)
您可以将其包装在您自己的界面中。请注意,在 C++14 中,std::array::operator[]是 constexpr,所以这只是
val_sum()(arr[Is]...);
Run Code Online (Sandbox Code Playgroud)
现在,回答你的问题:
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>{});
}
Run Code Online (Sandbox Code Playgroud)
依此类推,wheregen_seq_array将获取 A 的原始类型(std::remove_ref等std::remove_cv),推导出N,然后调用gen_seq<N>{}。如果数组元素具有移动语义,则转发有意义。它应该无处不在,例如上面的调用val_sum就像
val_sum()(std::get<Is>(std::forward<A>(a))...);
Run Code Online (Sandbox Code Playgroud)
2)返回类型:正如您所看到的,我使用它std::common_type作为返回类型,它应该用于sum最常见的算术运算。这已经是可变的了。如果您想要自己的类型函数,可以使用模板递归轻松地从二进制类型函数中创建变量。
我自己的版本common在这里。它有点复杂,但它仍然是一个递归模板,其中包含一些decltype用于完成实际工作的内容。
无论如何,这比您需要的更通用,因为它是为任意数量的任何给定类型定义的。您的数组中只有一种类型T,因此您所拥有的应该足够了。
| 归档时间: |
|
| 查看次数: |
504 次 |
| 最近记录: |