(これは古い日記です)

さて、比較的マニアックなC++の機能、というかライブラリの機能について書いてみようかと思う。

C++では関数型言語みたいな書き方ができるものがある。というヘッダを参照。

C++では100-3 は普通100-3 と書くが、次のようにも書ける。

std::minus<int>()(100, 3)

std::minusというのは、「-」を表す operator()() がオーバーロードされているクラステンプレート。

std::minus() は、コンストラクタを呼び出してオブジェクトを作成したもの。

これを関数オブジェクトと呼ぶ。

operator()() がオーバーロードされていることによって、std::minus()(100, 3)のように、オブジェクトを関数のように扱える。

一つ目の()は関数オブジェクトの作成のためのコンストラクタ呼び出しで、二つ目の()が、オーバーロードされたoperator()()の呼び出しになる。(operator()() の一つ目の()はオーバーロードする演算子の名前で、二つ目の()は普通の関数の()。ここでは関数呼び出し演算子()をオーバーロードしている。)

std::bind2nd(std::minus<int>(), 3)

bind2nd() とは、「第一引数に二項関数オブジェクトを、第二引数に値を取り、

第一引数に与えた二項関数オブジェクトの第二引数に、第二引数の値を束縛した、

単項関数オブジェクトを返す関数」。

つまり、

std::bind2nd(std::minus<int>(), 3)(100)

は、

std::minus<int>()(100, 3)

と同じような意味になる。こんなことに何の意味があるのかというと、

STLのコンテナ(データ構造)にアルゴリズムを適用する場合に使用する。

std::vector<int> input;
for(int i = 0; i < 10; i++) {
    input.push_back(i);
}
std::vector<int> output(input.size());
std::transform(input.begin(), input.end(), output.begin(), std::bind2nd(std::minus<int>(), 3));

input の中身は 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 で、output の中身は -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 になる。

コンテナ内の全ての要素に対して、要素-3を行う。bind2nd()はこの3を「-」の右辺に束縛するために使用している。

C++で書くと非常にわかりにくいので、Common Lispとの対比を。

上がC++、下がCommon Lispでの表記です。

100 - 3;  // C++
(- 100 3)  ; Common Lisp
std::minus<int>()(100, 3);  // C++
((lambda (x y) (- x y)) 100 3)  ; Common Lisp
std::bind2nd(std::minus<int>(), 3)(100);  // C++
((lambda (a)
  ((lambda (x y) (- x y)) a 3)) 100)  ; Common Lisp
std::transform(input.begin(), input.end(), output.begin(), std::bind2nd(std::minus<int>(), 3));  // C++
(mapcar #'(lambda (a) ((lambda (x y) (- x y)) a 3)) '(0 1 2 3 4 5 6 7 8 9))  ; Common Lisp

(lambda (x y) (- x y)) は、x, y の二つの引数を取り、x-yを返す無名関数。

mapcarは、第一引数に与えた関数に、第二引数に与えたリストの各要素を適用させる関数。

この日記を読んで、意味がわかる人がどれだけいるか不安です。(というか最後まで読む人がいるのだろうか…)

はじめはもっと簡単な内容にしようと思ったんですが、説明を省くと全くの意味不明になってしまうので、細かく書いていたらこんな感じになってしまいました。