結構C++0xの機能を書いてる気がするけど、全てを書くにはまだまだ大量の記事が必要になるレベル。
これだけの量なら遅れるのも仕方ない?
そんなことはさておき、今回は"Lambda functions and expressions"(ラムダ関数とラムダ式)。
ラムダ関数やラムダ式というのは、他の言語には標準で実装されていたりするので、「ああ、そういえばC++って標準で実装されてなかったね」という感じですが(私が大して他の言語でもラムダ式を使わないからでしょうが)、意外と使い慣れると便利になるかもしれません。
意外と説明することが多いので、まずは一番簡単な書き方から。
[](int x, int y) -> int { return x+y; }
これは、匿名関数で戻り値がint型である関数を意味します。
上の場合は、"return 式"の形の関数なので、次のように戻り値の型を省略することもできます。そのため、ラムダ関数が一行に制限されます。また戻り値の型は次の場合、decltype(x+y)となります。
[](int x, int y) -> { return x+y; }
returnがないラムダ関数は戻り値がvoid型だと解釈されます。
またクロージャを使うこともできます。クロージャとは、ラムダ関数と同じスコープで定義されている変数を参照することができます。たとえば次のようにラムダ関数で変数xの値を使うことができます。
int x = 0; [x](int z) -> int { int y = z+x; return ++y; }
また次のようなクロージャの使い方があります。
[] // 変数の参照はありません。 [x, &y] // xは値として、yは参照としてキャプチャされます。 [&] // 同じスコープで宣言された全ての変数(スタック変数)を参照します。 [=] // 同じスコープで宣言された全ての変数(スタック変数)を値としてキャプチャします。 [&, x] // xは値として、それ以外のスタック変数は参照としてキャプチャします。 [=, &z] // zは参照として、それ以外のスタック変数は値としてキャプチャします。
ただし、"&"をつけずに得た変数でも、ラムダ関数内だけならmutableキーワードを使うことで変更することができます。
int x = 1; /* 変数への格納については後述 */ auto func = [x]() -> int { std::cout << ++x << std::endl; } func(); /* 2が出力 */ func(); /* 2が出力 */
ところで、ラムダ関数をあるクラスのメンバ関数内で定義した場合、そのラムダ関数はそのクラスのフレンド関数となります。さらに次のようにクロージャを指定すると、そのクラスを参照することができます。しかし、明示的な指定が必要となります。
[this]() -> { this->memberFunc(); }
また"="や"&"でもthisを使うことができます。
また変数へラムダ関数を格納する場合は、コンパイラしかラムダ関数は型が分からないので、次のような方法を使って格納することが考えられます。
std::string x = "あいうえお\n"; /* autoを使う方法 */ auto printOfAuto = [x](const std::string& y) { std::cout << x << y << std::endl; }; /* std::functionを使う方法 */ std::function<std::string (const std::string&)> printOfFunction = [](const std::string& y) { return y; }; std::string message = printOfFunction("かきくけこ\n"); printOfAuto(message);
出力結果
あいうえお かきくけこ
ラムダ関数は不必要にコードを長くしないという利点があります。効率的にコードが書けるというわけです。