C++において、ラムダ式は便利な匿名関数であり、外部変数をキャプチャして内部で使用することができます。これにより、ラムダ式は柔軟なプログラミングツールとなります。しかし、ラムダ式のパラメータのライフサイクルは特に注意すべき点であり、特にキャプチャおよびパラメータを渡す際に重要です。
1. ラムダ式のパラメータのライフサイクル
ラムダ式のパラメータのライフサイクルは、通常他のC++関数と同様です。関数の引数は関数呼び出し時に存在し、関数呼び出しが終了すると引数のライフサイクルも終了します。ただし、ラムダ式が外部変数をキャプチャする場合、そのキャプチャ方法によって引数のライフサイクルに影響を受ける可能性があります。
2. パラメータのライフサイクルとの関係を捉える
2.1 外部変数のキャプチャ
C++のラムダ式では、以下の2つの方法で外部変数 キャプチャできます:
- 値によるキャプチャ: 値によるキャプチャでは、外部変数の値がラムダ内部にコピーされ、ラムダ内のコピーのライフサイクルはラムダのライフサイクルによって制御されます。
- 参照によるキャプチャ: 参照によるキャプチャでは、外部変数の参照が保持され、ラムダ内の参照は元の外部変数に指し示します。ライフサイクルは外部変数のライフサイクルに依存します。
int x = 10;
auto lambda_by_value = [x]() { std::cout << x << std::endl; }; // xのコピーをキャプチャ
auto lambda_by_reference = [&x]() { std::cout << x << std::endl; }; // xの参照をキャプチャ
lambda_by_value(); // 10 を出力
lambda_by_reference(); // 10 を出力
キャプチャされた変数 のライフサイクルは以下のとおりです:
- 値によるキャプチャ: キャプチャ時に外部変数の値がラムダにコピーされ、ラムダのライフサイクルが終了すると、コピーされたコピーが破棄されます。
- 参照によるキャプチャ: ラムダが外部変数の参照を保持し、外部変数はラムダの使用前に有効でなければなりません。そうでない場合、未定義動作が発生します。
2.2 ラムダパラメータ
ラムダパラメータは、通常の関数パラメータと同様に、そのライフサイクルはラムダ関数内のみに限られます。つまり、ラムダパラメータはラムダが呼び出されたときに作成され、ラムダが呼び出された後にそのライフサイクルも終了します。
auto lambda = [](int a, int b) {
std::cout << a + b << std::endl;
};
lambda(5, 10); // ここでaとbはラムダのパラメータ
この例では、a
と b
はラムダ式のパラメータであり、ラムダが呼び出されたときに作成され、ラムダの実行後に破棄されます。
3. 外部変数を捕捉する際のライフサイクルに関する問題
3.1 キャプチャされた変数が出 lambda の外で有効か
- 値によるキャプチャ:外部変数が lambda が呼び出された後に破棄されても、lambda 内には外部変数のコピーが保持されます。したがって、lambda 内のコピーは安全に使用でき、外部変数が存在しなくなっても問題ありません。
int x = 10;
auto lambda = [x]() { std::cout << x << std::endl; };
x = 20; // x は lambda が呼び出された後に変更されます
lambda(); // 10 を出力します(x のコピーを使用)
- 参照によるキャプチャ:外部変数を参照でキャプチャする場合、lambda 内での外部変数へのアクセスは外部変数のライフサイクルに依存します。外部変数が lambda が実行される前に破棄された場合、ダングリング参照が発生し、未定義の動作を引き起こります。
int x = 10;
auto lambda = [&x]() { std::cout << x << std::endl; };
x = 20; // x は lambda が実行される前に変更されます
lambda(); // 20 を出力します(x の参照を使用)
lambda の実行順序が不定の場合、キャプチャされた外部変数が lambda の実行時に有効であることを保証することが重要です。