関数の引数をbindする

はじめに

関数の引数をbind(束縛)して新しい名前をつけると便利な場合があるかもしれない。 例えばxを三乗する関数cubic(x)を作りたいとき、素直に定義したり毎回pow(x,3.0)と書いてもよいが次のように書ければスマートだ。

cubic = bind(pow, 3.0); // 引数束縛的な何か
for(int i=0;i<10;i++)
  printf("%lf\n", cubic(x));

これは関数型言語の世界では当たり前に行われており、例えばschemeでは次のようにかける。

(define cubic (lambda (x) (expt x 3))) ; (expt x 3)はxの3乗を返す

std::bind1st, std::bind2nd

実は引数のbindはC++でもSTLのbind1st及びbind2ndを使えばほぼ最初の擬似コードと同様に書ける(ただし任意の関数に対してはできず1引数または2引数関数しかできない)。 本ページの目的はSTLの使い方を述べることではないのでこれらの説明は適当にぐぐっていただきたい。

bindfirst(std::bind1stもどき)

2引数関数の第一引数をbindするbindfirstは例えば以下のように実装できる。 tripleは2引数関数fの第一引数を3でbindしたもので、結局は入力を3倍する1引数関数(オブジェクト)となる。

#include <iostream>

using namespace std;

template <typename T, typename U, typename R> class bindfirst{
private:
  T bounded;
  R (*f)(T, U);

public:
  bindfirst(R (*f)(T, U), T first_arg){
    bounded = first_arg;
    this->f = f;
  }

  // call f with first argument bounded by `bounded'
  R operator() (U second_arg){
    return f(bounded, second_arg);
  }
};

int f(int a, int b){
  return a * b;
}

int main(){
  bindfirst<int, int, int> triple = bindfirst<int, int, int>(f, 3);

  // 0 3 6 9 12 ... 27                        
  for(int i=0;i<10;i++)
    cout << triple(i) << endl;
}

要は関数ポインタとbindする引数をクラスに閉じ込めているだけであるが、Templateの存在によって1実装で引数の型を問わず使えるのがポイントである。 bindsecond(std::bind2ndもどき)もoperator ()の定義をかえるだけで同様に実装でき、これを用いて冒頭のcubicも実現できるので試してみて欲しい。

なおbindfirstでは関数をクラスに閉じ込めているが、std::bind1stでは関数オブジェクトを閉じ込めている点が異なる。ついでに多分引数などは即値ではなく参照をとるようになっているかもしれないがそれらは瑣末な差である。