What is it, naokirin?

C++0xの機能 (ラムダ関数とラムダ式)

結構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);

出力結果

あいうえお
かきくけこ




ラムダ関数は不必要にコードを長くしないという利点があります。効率的にコードが書けるというわけです。