和算法配套出现的组件除了迭代器之外还有仿函数,这篇文章会重点介绍仿函数的使用以及和它相关的函数式编程工具。
算法库中有很多算法都有一个重载的版本,接收一个Callable object
用于提升算法的灵活性。
template< class InputIt, class UnaryPredicate >
InputIt find_if( InputIt first, InputIt last,
UnaryPredicate p );
template< class InputIt, class T, class BinaryOperation >
T accumulate( InputIt first, InputIt last, T init,
BinaryOperation op );
语法上可调用对象(Callable object
)是指可以使用函数调用符号操作它的任何对象,它可以表示很多对象,比如普通的函数,也比如一个重载了函数调用符的类型对象。如果可调用对象的返回值是bool
则称之为Predicate
(有人翻译成谓词)。接收Predicate
的算法通常都以_if
结尾,比如:std::find_if
,std::copy_if
。
我们把重载了函数调用符的对象称为仿函数,它在功能上是一个函数,在语法上是一个类。它和普通函数最大的区别是它可以保存内部状态。
假如我们现在要找出第一个奇数,我们可以这样写:
bool is_odd(int a) { return a % 2; }
std::find_if(std::begin(a), std::end(a), is_odd)
假如我们要查找第一个偶数,我们可以写成这样:
bool is_even(int a) { return !is_odd(a); }
std::find_if(std::begin(a), std::end(a), is_even)
但是如果我实现成仿函数,我们可以这样写:
class FindOdd {
public:
NumberPredicate(bool found_odd) { ... }
bool operator()(int a) const {
if (found_odd_) {
...
}
...
}
private:
bool found_odd_;
};
std::find_if(std::begin(a), std::end(a), FindOdd(true));
std::find_if(std::begin(a), std::end(a), FindOdd(false));
上面这个例子其实并不太恰当,我不推荐在一个函数中实现两个功能,但是它展示了仿函数区别于普通函数的重要特性——可以保存状态。
Predicate
是指返回值为bool
的Callable object
,而纯函数是指这个对象不会有side effect
,也就是说如果以同样的参数调用这个对象,结果是一样的。之所以这么建议是因为Predicate
作为算法的参数而存在,而参数的传递是值拷贝,如果Predicate<