(これは古い日記です)
さて、比較的マニアックなC++の機能、というかライブラリの機能について書いてみようかと思う。
C++では関数型言語みたいな書き方ができるものがある。
C++では100-3 は普通100-3 と書くが、次のようにも書ける。
std::minus<int>()(100, 3)
std::minusというのは、「-」を表す operator()() がオーバーロードされているクラステンプレート。
std::minus
これを関数オブジェクトと呼ぶ。
operator()() がオーバーロードされていることによって、std::minus
一つ目の()は関数オブジェクトの作成のためのコンストラクタ呼び出しで、二つ目の()が、オーバーロードされた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は、第一引数に与えた関数に、第二引数に与えたリストの各要素を適用させる関数。
この日記を読んで、意味がわかる人がどれだけいるか不安です。(というか最後まで読む人がいるのだろうか…)
はじめはもっと簡単な内容にしようと思ったんですが、説明を省くと全くの意味不明になってしまうので、細かく書いていたらこんな感じになってしまいました。