std::bind を使って関数を特殊化する - C++ プログラミング

PROGRAM


std::bind を使って関数を特殊化する

C++ では、既存の関数にあらかじめ引数を割り当てることで、その用途を限定できるようになっています。

 

たとえば、商品 ID 毎に説明文や値段などの情報を記録するシステムがあったとき、対象の商品 ID を指定して、それらの情報を書き換える関数セットを用意したとします。

このとき、商品毎に管理する人をひとり決めて、その人に関数セットを使って自分が担当する商品の情報を編集してもらおうと思ったとき、関数セットをそのまま皆に渡してしまうと、誰かが自分の商品 ID を間違えたとき、他の誰かが管理する商品の情報を間違っていじってしまうことになります。

そんなとき、商品 ID を指定する部分を、各人毎にその人用の ID に固定した関数セットを渡してあげれば、誤って他の ID を使ってしまう心配もないし、各人毎に関数を再実装する手間も省けます。

 

C++ で関数の引数を固定化して、限定的な関数を作り出すには std::bind 関数を使用します。

 

準備

std::bind 関数は <functional> ヘッダーに定義されているので、まずはそれをどこかにインクルードしておきます。

#include <functional>

 

使用方法

たとえば、次の 2 つの関数が定義されているとします。

void setDescriptionForItemID(int itemID, std::string description);

 

std::string getDescriptionForItemID(int itemID);

これらの itemID を固定した関数を std::bind 関数を使って作成します。

 

たとえば itemID を 1 に固定した関数を作成する場合は、次のようになります。

std::function<void(std::string)> setDescription =  std::bind(setDescriptionForItemID, 1, std::placeholders::_1);

 

std::function<std::string()> getDescription = std::bind(getDescriptionForItemID, 1);

このように std::bind で作成した関数は、適切な std::function に格納することができます。

もちろん C++11 の型推論で変数定義を簡略化する に記した auto 型でも大丈夫です。

 

std::bind 関数では、最初の引数で対象の関数ポインタを指定します。続く引数で対象関数に渡す引数を指定しています。

ここで、今回の例では "itemID" に該当する引数を "1" にすることで、作成される関数を呼び出したときに、この引数が "1" で固定されるようになります。

ここには変数も指定できて、変数を std::bind 関数の引数部分に渡したときには、その変数の値がコピーされるようでした。

ラムダ関数 で言うところの "&" を付けないクロージャーといったところでしょうか。

 

また、std::bind 関数を呼び出す中で "std::placeholders::_1" と指定されているところがあります。

これは、std::bind 関数で新しく作成した関数に指定した引数をここに渡す、という意味になります。"_1" となっているのが第一引数として渡された引数で、第二引数であれば "_2" に、第三引数であれば "_3" といった具合になります。

これは順番も自由に指定できて、例えば std::bind(function, std::placeholders::_2, std::placeholders::_1) というようにすれば、ここで作成された関数の第一引数が function 関数の第二引数に、作成された関数の第二引数が function 関数の第一引数に渡されます。

 

なお、これらの作成した関数に渡した引数は、std::bind で指定した関数(この例では function)に、std::forward 関数を使ってそのままの形で転送されるそうです。

 

std::bind で扱いたい関数がオーバーロードされている場合

std::bind の第一引数で渡したい関数がオーバーロードされている場合は、関数名だけだとどの引数を取る関数かが判らないため、たとえば Xcode 4.6.2 では "No matching function for call to 'bind'" というエラーになります。

このようなときは、第一引数に渡す関数を、目的の関数ポインタ型で明示的にキャストする必要があります。

関数ポインタ型でキャストする方法については 関数のポインタを使用する を参照してください。


[ もどる ]