【C++ / Boost】boost::bindについての適当まとめ
何語だこれ…状態になったのでメモしておきます。
まずはboost::functionから。
#include <iostream> #include <boost/function.hpp> typedef boost::function<int(int, int)> TestFunction; int sum(int a, int b) { return a + b; } int main() { TestFunction Tf; int Result = 0; Tf = ∑ Result = Tf(2, 5); std::cout << "結果は" << Result << "でした。" << std::endl; return 0; }
こんな感じです。Resultは7になります。このboost:functionを使用することで、簡単に関数ポインタに近い働きをさせることができます。
また、他にも器用な点があります。関数ポインタの場合、上記の例のように普通の関数の場合なら何の問題もないのですが、クラス内の関数のポインタとなると、うまく扱うことができません。具体的には、
// doubleを戻り値とし、intを引数とする関数のポインタ。 double (*pFunc)(int) = &func; // doubleを戻り値とし、intを引数とするCSampleメンバ内の関数のポインタ。 double (CSample::*pFunc)(int) = &CSample::func;
のように、戻り値の型が違うために、不便となるケースがでてきます。
この問題点をboost::functionは解決してくれる、というわけです。
具体的には以下のようになります。
#include <iostream> #include <boost/bind.hpp> #include <boost/function.hpp> typedef boost::function<int()> TestFunction; class Sample { public: Sample() { Val = 5; } int GetVal() { return Val; } int Val; }; int main() { TestFunction Tf; Sample Smp; int Result = 0; // ここでboost::bindを使用しています。 Tf = boost::bind(&Sample::GetVal, Smp); Result = Tf(); std::cout << "結果は" << Result << "でした。" << std::endl; return 0; }
ここでboost::bindが出てきます。第一引数が実行する対象の関数、第二引数が対象のクラスです。メンバ関数は(当然ですが)自身のメンバにアクセスしたりする関係上、そのクラスが存在してないと動きませんので、その対象となるクラスを指定します。
ちなみに、メンバ関数のポインタではなく、普通の関数のポインタも取れます。
#include <iostream> #include <boost/bind.hpp> #include <boost/function.hpp> typedef boost::function<int()> TestFunction; int GetVal() { return 10; } int main() { TestFunction Tf; int Result = 0; // ここでboost::bindを使用しています。 // ここは Tf = &GetVal; でも動作します。 Tf = boost::bind(&GetVal); Result = Tf(); std::cout << "結果は" << Result << "でした。" << std::endl; return 0; }
boost::bindを使用して引数がある関数を扱う場合、少々ややこしくなります。
#include <iostream> #include <boost/bind.hpp> #include <boost/function.hpp> typedef boost::function<int(int, int)> TestFunction; int Sum(int a, int b) { return a + b; } int main() { TestFunction Tf; int Result = 0; // ここでboost::bindを使用しています。 // 引数に_1, _2が使われています。 Tf = boost::bind(&Sum, _1, _2); Result = Tf(2, 5); std::cout << "結果は" << Result << "でした。" << std::endl; return 0; }
コメントで説明しているように、引数に_1や_2が使われています。これは「関数を実行するときに引数として渡しますよ」という目印です。しかし、なぜこのような回りくどいことをしなければならないのでしょう。
それは、「引数を固定できる」機能を持っているからです。…ちょっと何言ってるか分かりませんね。コードを見てみます。
#include <iostream> #include <boost/bind.hpp> #include <boost/function.hpp> // <int(int, int)>ではなく<int(int)>となっている。 typedef boost::function<int(int)> TestFunction; int Sum(int a, int b) { return a + b; } int main() { TestFunction Tf; int Result = 0; // _1, _2だったのが4, _1となっている。 // これは第一引数を4で固定したこととなる。 Tf = boost::bind(&Sum, 4, _1); // 引数の数が1つになっている。 Result = Tf(5); std::cout << "結果は" << Result << "でした。" << std::endl; return 0; }
boost::bind関数が(_1, _2)ではなく、(4, _1)となっています。本来Sum()関数は引数が2つ必要ですが、最初に「1つ目は4を入れてしまおう」と決め打ちすることで、2つ必要な変数入力を1つに抑えています。今回の例だと有り難みは分かりにくいですが、実際にはこの技術を応用することでかなり複雑なコードを書くことができます。
また、boost::functionの<>の中身が先ほどまでと違います。これは、引数が一つで済むからです。
このように、使いこなせれば便利ですが、エラーの中身が意味分からないという欠点もあります。例えば、上記のコードで言えば、
Tf = boost::bind(&Sum, 4, _1);
を
Tf = boost::bind(&Sum, 4, _2);
と書いてしまうと
error C2118: 添字が負の数です。
と文句を言われます。ですが、もちろん添字と理解して使っているわけではないので、エラーの発生源がとても特定しにくくなる欠点が存在します。