谓词函数是一个判断式,一个返回bool值的函数或者仿函数。
C++ standard library P264:并非所有返回布尔值的仿函数都是合法的谓词函数。使用两个相同的参数调用一个一元谓词函数,应该总是返回相同的结果(与调用次数无关)。
使用以下代码移除一组元素中的第三个元素,却发生了意想不到的事情。
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
template <class T>
void print_elements(const T& container){
for(typename T::const_iterator it = container.begin(); it != container.end(); ++it){
cout << *(it) << " ";
}
cout << endl;
}
class Nth{
private:
int nth, count;
public:
Nth(int n): nth(n), count(0) {}
int operator() (int){
return nth == ++count;
}
};
int main(){
list<int> coll;
for (int i = 0; i < 9; ++i)
coll.push_back(i);
print_elements(coll);
list<int>::iterator pos;
pos = remove_if(coll.begin(), coll.end(), Nth(3));
coll.erase(pos, coll.end());
print_elements(coll);
return 0;
}
运行结果为:
coll: 1 2 3 4 5 6 7 8 9
nth removed: 1 2 4 5 7 8 9
为什么会产生这样的结果呢?这是因为remove_if内部实现中,首先调用了find_if函数来查找第一个满足pred的迭代器位置。然后再处理剩余的元素,代码如下:
template<class ForwardIterator, class Predicate>
ForwardIterator std::remove_if(ForwardIterator beg, ForwardIterator end, Predicate op)
{
beg = find_if(beg, end, op);
if(beg == end){
return beg;
}
else{
ForwardIterator next = beg;
return remove_copy_if(++next, end, beg, op);
}
}
可以看到,对后续元素调用remove_copy_if,将谓词函数返回false的元素copy至beg处,而谓词函数是以传值方式传递的,所以在remove_copy_if中,第三个元素也会被remove。
所以,一个合法的谓词函数,它的行为不应该取决于被调用次数,谓词函数不应该因为被调用而改变自身状态。故一般情况下应该将operator()声明为const成员函数。
敲代码的时候犯了一个小错误,写仿函数的operator()时,由于操作符中并未使用额外的参数,所以函数定义时默认成了不传参,结果编译错误。
for(; __first != __last; ++__first)
if(!bool(__pred(*__first)))
{
*__result = _GLIBCXX_MOVE(*__first);
++__result;
}
可以看到,在remove_if算法中,使用谓词函数时传入了*(__first),所以谓词函数定义时,形参应与容器元素类型相同,更不能省略。