谓词函数predicates和仿函数functors

6 篇文章 0 订阅

谓词函数是一个判断式,一个返回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),所以谓词函数定义时,形参应与容器元素类型相同,更不能省略。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值