reduce() 和 accumulate() 都是用于对集合(比如数组)中元素进行归约操作:
accumulate用于对迭代器指示范围[first, last)中元素进行累加操作,对于序列当中的每个元素,一次应用某个二元操作(默认加法),并且将结果累加到一个初始值上。函数原型如下:
template<class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init);
template<class InputIt, class T, class BinaryOperation>
T accumulate(InputIt first, InputIt last, T init, BinaryOperation op);
用法的示例:
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// 使用默认的加法操作
int sum = std::accumulate(v.begin(), v.end(), 0);
std::cout << "Sum: " << sum << std::endl; // 输出: Sum: 15
// 使用乘法操作
int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
std::cout << "Product: " << product << std::endl; // 输出: Product: 120
return 0;
}
reduce 同样是对迭代器指示范围内元素进行归约操作,但支持并行执行,操作元素必须是无关联的。函数原型:
template<class InputIt>
typename std::iterator_traits<InputIt>::value_type
reduce(InputIt first, InputIt last);
template<class InputIt, class T>
T reduce(InputIt first, InputIt last, T init);
template<class InputIt, class T, class BinaryOperation>
T reduce(InputIt first, InputIt last, T init, BinaryOperation binary_op);
template<class ExecutionPolicy, class ForwardIt>
typename std::iterator_traits<ForwardIt>::value_type
reduce(ExecutionPolicy&& policy, ForwardIt first, ForwardIt last);
template<class ExecutionPolicy, class ForwardIt, class T>
T reduce(ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, T init);
template<class ExecutionPolicy, class ForwardIt, class T, class BinaryOperation>
T reduce(ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, T init, BinaryOperation binary_op);
使用的例子:
#include <iostream>
#include <numeric>
#include <vector>
#include <execution> // 需要包含以使用并行执行策略
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// 使用默认的加法操作
int sum = std::reduce(v.begin(), v.end());
std::cout << "Sum: " << sum << std::endl; // 输出: Sum: 15
// 使用乘法操作
int product = std::reduce(v.begin(), v.end(), 1, std::multiplies<int>());
std::cout << "Product: " << product << std::endl; // 输出: Product: 120
// 使用并行执行策略
int parallel_sum = std::reduce(std::execution::par, v.begin(), v.end());
std::cout << "Parallel Sum: " << parallel_sum << std::endl; // 输出: Parallel Sum: 15
return 0;
}
上述函数在使用的时候,如果需要指示运算的类型,则需要输入函数对象,比如:
std::multiplies<int>()
std::plus<int>()
std::minus<int>()
std::divides<int>()
那么为什么需要这么写呢?
在reduce accumulate 函数当中为什么不能直接写multiplies<int>,非得multiplies<int>()?
因为前者是函数类型(模板类),后者是函数对象(临时对象,类的实例化)。
模板类 multiplies<int>
是一个类型,它定义了如何执行乘法操作。要使用这个操作,我们需要一个具体的实例——一个对象。multiplies<int>()
就是创造了这样一个对象。reduce accumulate期望接收一个可调用对象(即函数对象、普通函数或 lambda 表达式)作为参数。类本身不能作为可调用对象传递,必须传递一个实例化的对象。
可以简单理解为reduce accumulate需要一个能够直接调用的对象,要么是类的实例要么是函数。常用的一些函数同样如此,比如sort当中,常用的写法是匿名函数:
ranges::sort(nums, [](int a, int b){
return a > b;
});
下面是一些辅助知识:
函数对象是可以像函数一样调用的对象。可通过重载 operator()
运算符来实现这一功能。如 accumulate
和 reduce
,都可以接受函数对象作为参数,以指定用于归约操作的二元运算:
struct Multiply {
int operator()(int a, int b) const {
return a * b;
}
};
Multiply multiply;
int result = multiply(3, 4); // 等价于 3 * 4
在底层实现上,std::multiplies<int>
是一个模板类的实例化,重载了 operator()
运算符,使得实例对象可以像函数一样调用。这种设计使得标准库函数能够接受函数对象作为参数:
namespace std {
template<typename T>
struct multiplies {
T operator()(const T& lhs, const T& rhs) const {
return lhs * rhs;
}
};
}