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番目の要素も削除されてしまいます。
対策は、述語を純粋関数にすることです。