在C++中,纯函数所能访问的数据应该仅限于参数和常量(在函数的周期中不会被改变,自然的,这样的常量应该被声明为const).如果一个纯函数需要访问那些可能在两次调用之间发生变化的数据,那么用相同的参数在不同的时刻调用该函数就有可能得到不同的结果。
判别式类:是一个函数的子类,他的operator()函数是一个判别式,也就是说,他的operator()返回true或者false.正如你所料,STL中凡事能接受一个判别式的地方,就既能接受一个真的判别式,也可以接受一个判别式类的对象。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//在第三次的时候返回true,其余返回false
class BadPredicate:public unary_function<int,bool>
{
public:
BadPredicate() :timesCalled(0){};
bool operator()(const int&)
{
return ++timesCalled == 3;
}
private:
size_t timesCalled;
};
int main()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
v.erase(remove_if(v.begin(), v.end(), BadPredicate()), v.end());
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << endl;
}
//输出的结果是0、1、3、4、6 7 8 9
return 0;
}
上边的代码结果是不仅删除了第三个元素,还删除了第六个元素
下边是原因:
template<class _FwdIt,
class _Pr> inline
_FwdIt remove_if(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
{ // remove each satisfying _Pred
_First = _STD find_if(_First, _Last, _Pred);
if (_First == _Last)
return (_First); // empty sequence, all done
else
return (_Rechecked(_First,
_Remove_if(_Unchecked(_First), _Unchecked(_Last), _Pred)));
}
上边这段代码是本地代码库中STL中remove_if的实现方法,从中可以看出:find_if中调用的P是一份拷贝 ,之后的里边又是一份拷贝,两次初始值timesCalled都为0,所以最后删除了2次。
最简单的使你自己不摔跟头而进入语言陷阱的方法是在判断式类中把你的operator()函数声明为const。如果你这么做了,你的编译器不会让你改变任何类数据成员。
//在第三次的时候返回true,其余返回false
class BadPredicate:public unary_function<int,bool>
{
public:
BadPredicate() :timesCalled(0){};
bool operator()(const int&) const//错误,因为const成员函数不能修改任何数据成员
{
return ++timesCalled == 3;
}
private:
size_t timesCalled;
};
但那走得不够远。甚至const成员函数可以访问multable数据成员、非const局部静态对象、非const类静态对象、名字空间域的非const对象和非const全局对象。一个设计良好的判断式类也保证它的operator()函数独立于任何那类对象。在判断式类中把operator()声明为const对于正确的行为来说是必要的,但不够充分。一个行为良好的operator()当然是const,但不只如此。它也得是一个纯函数。