[6 函数子类及函数] 39. 确保Predicate判别式是纯函数

先引入几个概念:

一个判别式(predicate)是一个返回值为bool类型(或可以隐式转换为bool)的函数。

一个纯函数(pure function)是指返回值仅仅依赖于其参数的函数。C++中,纯函数所能访问的数据应该仅局限于参数以及变量。

判别式类(predicate class)是一个函数子类,它的operator()函数是一个判别式,STL中凡是能接受判别式的地方,也可以接受一个判别式类的对象。

我们先看看违反此约束的后果,考虑以下设计拙劣的判别式类,它在第3次被调用的时候返回true,其余的调用均返回false:

class BadPredicate: public unary_function<Widget, bool> {
public:
    BadPredicate():timesCalled(0) {}
    bool operator()(const Widget&)
    {
        return ++timesCalled == 3;
    }        
private:
    size_t timesCalled;
};

假设我们使用这个判别式来删除vector<Widget>中的第3个Widget:

vector<Widget> vw;
...
// 删除第3个元素
vw.erase(remove_if(vw.begin(), vw.end(), BadPredicate()), vw.end());

代码看似是正确的,实际上,它不仅删除了vw容器中的第3个元素,而且还删除了第6个元素。假如remove_if的一种实现:

template<typename FwdIterator, typename Predicate>
FwdIterator remove_if(FwdIterator begin, FwdIterator end, Predicate p)
{
    // p按值传递
    begin = find_if(begin, end, p);
    if (begin == end) return begin;
    else {
        FwdIterator next = begin;
        // p按值传递
        return remove_copy_if(next++, end, begin, p);
    }
}

在两次传参过程中,p都是按值传递的,也就是说是复制的。每次传递,会导致p的成员timesCalled为0。所以上述实现会导致不仅删除第3个元素,也会删除第6个元素。

STL中凡是可以接受一个判别式类的地方都可以接受一个判别式函数。所以注意,BadPredicate()改写为判别式函数也是错误的!如下:

bool BadPredicate(const Widget& w)
{
    static int timesCalled = 0;
    return ++timesCalled == 3;
}

猜你喜欢

转载自blog.csdn.net/u012906122/article/details/119808241
今日推荐