関数オブジェクトは奥が深い
はじめての関数オブジェクト体験。
動かん
あるvectorの要素で、他のvector中にも入っているやつを取り除こうとして次のようなコードを書いたらコンパイルエラーになりました。
#include <iostream> #include <vector> #include <algorithm> #include <functional> bool contains(const std::vector<int> v, int x){ return std::find(v.begin(), v.end(), x) != v.end(); } int main(){ std::vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(5); v1.push_back(6); std::vector<int> v2; v2.push_back(3); v2.push_back(4); v2.push_back(6); v2.push_back(8); v2.push_back(9); // v1の要素で、v2中にもあるやつを除去する std::vector<int>::iterator newEnd = std::remove_if(v1.begin(), v1.end(), std::bind1st(contains, v2)); // コンパイルエラー v1.erase(newEnd, v1.end()); }
std::binary_function、std::ptr_fun
間違っていたのは、std::bind1st の引数に渡す関数オブジェクトは std::binary_function を継承していなくてはならないため、単純な関数ポインタそのままは渡せない、というところ。
リファレンス:MSDN - ptr_fun
// v1の要素で、v2中にもあるやつを除去する std::vector<int>::iterator newEnd = std::remove_if(v1.begin(), v1.end(), std::bind1st(std::ptr_fun(contains), v2)); // OK v1.erase(newEnd, v1.end()); for(std::vector<int>::iterator itr = v1.begin(); itr != v1.end(); ++itr){ std::cout << *itr << " "; }
出力:
1 2 5
いつも必要というわけではない
紛らわしいのが、std::remove_if の第3引数自体には単純な関数ポインタを渡せること。
#include <iostream> #include <vector> #include <algorithm> #include <functional> bool even(int i){ return i % 2 == 0; } int main(){ std::vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(5); v1.push_back(6); // 偶数のやつだけ取り除く std::vector<int>::iterator newEnd = std::remove_if(v1.begin(), v1.end(), even); // evenはただの関数ポインタ v1.erase(newEnd, v1.end()); for(std::vector<int>::iterator itr = v1.begin(); itr != v1.end(); ++itr){ std::cout << *itr << " "; } }
出力:
1 3 5
関数オブジェクトを使うときは、そいつが std::binary_function や std::unary_function を継承してないといけないのか、ただの関数ポインタでもいいのか(つまり、operator()で呼び出せさえすればいいのか)を気をつけて見とかないとといけないのだな。
ややこしいね。