39項 述語を純粋関数にしよう

述語を純粋関数にしよう



述語とは、bool(または暗黙的にboolに変換できる値)を返す関数です。find_ifなどさまざまなアルゴリズムに引数として渡せます。
純粋関数は、戻り値がパラメータだけにしか依存しない関数です。


remove_ifに非純粋述語クラスを渡すことを考えます。

template<typename T>
class BadPredicate : public unary_function<T, bool>
{
public:
	BadPredicate(): timesCalled(0){}
	bool operator()(const T &)
	{
		return ++timesCalled == 3; //3回目の呼び出しにtrueを返す。
	}
private:
size_t timesCalled;
};


vector<int> v;
...

v.erase(remove_if(v.begin(), v.end(), BadPredicate<int>()), v.end()); //3番めの要素だけを削除させたい

eraseとremove_ifの呼び出しで3番めの要素だけを削除しようとしたコードですが、6番目の要素も削除される可能性が高いです。


原因は、remove_ifが下のように実装されていることが多いからです。

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

remove_ifの内部でremove_copy_ifを呼び出しています。
この時、pを値渡しするため、コンストラクタが呼び出されtimesCalledが0になり、6番目の要素も削除されてしまいます。


対策は、述語を純粋関数にすることです。