lambda 式は、C++11 以降のバージョンで、匿名関数を定義するための簡潔な構文です。STL アルゴリズムや非同期操作など、いくつかの長い環境では、コールバック関数として lambda を使用することが非常に自然で便利であり、lambda と C++ の関数型プログラミングの特性は非常に適合しています。
基本構文#
完全な lambda 式の構文は次のようになります:
[キャプチャリスト](パラメータリスト) mutable throw() -> 戻り値の型 {
// 何かを行う..
}
キャプチャリスト#
lambda は、中括弧で囲まれたキャプチャリストで始まります。キャプチャリストは、lambda 式が使用できる外部変数とその使用方法(値渡し、参照)を定義するために使用されます。
キャプチャリストは空にすることもできますが、中括弧は省略できません。
値渡しまたは参照の方法に関係なく、デフォルトのキャプチャ方法を使用することもできます。デフォルトの値渡しキャプチャ方法とデフォルトの参照キャプチャ方法は混在させることができます。具体的な使用法は以下の通りです:
[=] // デフォルトの値渡しキャプチャ
[&] // デフォルトの参照キャプチャ
[&, factor] // factor以外は参照キャプチャを使用する。
[=. factor] // factor以外は値渡しキャプチャを使用する。
キャプチャリストでは、重複した宣言は許可されません。この重複には、キャプチャ方法の重複と変数の重複が含まれます:
[&, &factor] // エラー、&は既にデフォルトのキャプチャ方法を宣言しています。&factorは重複しています。
[=, factor] // 同上
[factor, factor] // エラー、変数が重複しています。
自動キャプチャは、より便利なキャプチャメカニズムを提供しますが、不要な変数をキャプチャする可能性があり、メモリ使用量の増加やパフォーマンスの問題を引き起こす可能性があります。
値渡しまたは参照キャプチャの選択は、通常、特定のシナリオに基づいて行われます。一般的には、値渡しまたは参照キャプチャの選択は、通常の関数の引数の選択とほぼ同じルールに従います。
キャプチャリストは可変引数テンプレートも使用できます:
[arg...]
クラスのメンバ関数本体で lambda 式を使用する場合、this
ポインタを明示的にキャプチャする必要があります。これにより、クラスのメンバ関数とデータへのアクセス権が提供されます。
[this, filter]
C++14 では、新しいデフォルトキャプチャ方法が導入されました。これにより、キャプチャ句に新しい変数を導入して初期化することができますが、これらの変数をクロージャ内に配置する必要はありません。これは、スコープとライフサイクルを考慮する必要がある変数に非常に便利です:
auto p = make_uniqure<T>(factor);
auto _lambda = [ptr = move(p)]{}
パラメータリスト#
lambda のパラメータリストは、通常の関数のパラメータリストとほぼ同じように動作します。
キャプチャリストとパラメータリストの両方は、クロージャに変数を提供するために使用できますが、キャプチャリストは、lambda の定義時に既知であり、後続の呼び出しで変更されない変数に適用されます。
キャプチャリストは、lambda の宣言期間全体で変わらない変数に適用され、パラメータリストは、lambda を呼び出すたびに異なる変数に適用されます。
std::vector<int> num = {1,2,3,4};
int fixedValue = 10;
std::for_each(num.begin(). num.end(), [fixedValue](int &x){x += fixedValue;});
パラメータリストでは、パラメータの型がジェネリックである場合、auto を使用してコンパイラにテンプレートを作成するように指示することができます:
[](auto factor)
mutable#
通常、lambda の関数呼び出しは const-by-value ですが、mutable 指定子を追加すると、クロージャは値キャプチャされた変数を変更できます:
例外#
throw()
は、lambda が例外をスローすることを示すために使用され、省略することも、noexcept を使用して例外をスローしないことを示すこともできます:
[]() noexcpet {}
[]() throw() {}
[]() {}
戻り値の型#
lambda の中で戻り値の型を指定することは省略することができ、コンパイラによる自動推論に任せることもできますが、例外もあります:
// C++11以前の一部のコンパイラ
[](int i){
if (i < 0){
return i;
}
else{
return -i;
}
} // エラー、推論型はvoidです
これは、クロージャのoperator()
の推論ルールによるものです:
本体に return 文が 1 つだけ含まれている場合、戻り値の型は式の型です。それ以外の場合、戻り値の型は void です。 (C++14 以前)。
lambda 本体#
基本的な動作は通常の関数と同じです。