"constexpr"通过二进制操作减少"std::array"
`constexpr` reduction of `std::array` with binary operation
我想写一个constexpr函数,它通过二进制操作减少给定的std::array
。 即实现
template <typename T, std::size_t N>
reduce(std::array<T, N>, binary_function);
为了简单起见,我想从加法开始。
例如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>
然后通过可变参数模板递归定义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>{});
}
在这里,您可以看到它的实际效果。
问题
此实现有效。但是,现阶段我确实有几个问题。
- 有什么办法,我可以为这个功能添加完美转发吗?这甚至有意义吗?或者我应该声明这些数组 const-references?
- 到目前为止的假设是,减少的返回类型是
decltype(T()+T())
。 即当你添加两个元素时你得到什么。虽然在大多数情况下加法应该是这样,但对于一般减少来说可能不再如此。有没有办法获得a[0] + (a[1] + (a[2] + ... ) )
的类型?我尝试了这样的事情,但我不知道如何生成<T, T, T, ...>
的模板参数列表。
我的回答是基于我自己对此类工作人员的实现。
我更喜欢通用的reduce(或折叠或累加)函数直接对元素进行操作,作为它自己的函数参数,而不是像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)...));
}
};
很抱歉,这充满了我自己的定义,所以这里有一些帮助: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
版本在这里。它涉及更多,但它仍然是一个递归模板,其中包含一些执行实际工作decltype
。
在任何情况下,这比您需要的更通用,因为它是为任意数量的任何给定类型定义的。数组中只有一个类型T
,所以你所拥有的应该足够了。
- C++11 中不同类型的对象的 std::array 的替代方案
- constexpr begin of a std::array
- C++如果必须在编译时确定大小,std::array 有什么意义?
- OpenGL VBO Indexing ( How to compute Index Array)
- 标准::unordered_map 中的 std::array 的值初始化
- "Warning: Comma within array index expression"但逗号分隔函数参数
- 确保编译时的特定 std::array 位置
- std::array的长度有大小限制吗?
- 将 std::array 移动到另一个 std::array
- 首先按给定顺序打印所有数字,然后使用 Array 打印所有字符和其他符号
- 为什么 std::shared_ptr 被认为是"heavy"和"expensive",但 std::array "same perfprmance as plain (c-style) arrays
- 将 **float array 从 C++ Dll 传递给 python
- std::bind on statd::array 的运算符 []
- 检查输入 std::array 指针数据是否等于某个常量数组
- 我可以安全地复制矢量<array>吗?
- 解析问题 - 预期的非限定 ID - #include <array> 编译错误
- 如何读/写或遍历 std::array 中的特定元素范围?
- 通过 host() 从 af::array 检索数据会导致错误的数据
- 是否可以使用 std::array 作为 POD 结构的数据容器?
- 如何在C++中传递一个大小未知的 std::array?