導入: なぜmutableが必要なのか
C++のラムダ式を使用する際、外部の変数をキャプチャして利用することは非常に一般的です。しかし、通常の方法で値をコピーキャプチャすると、ラムダ式の内部ではその変数は「読み取り専用(const)」として扱われます。もし、「ラムダ式の中で状態を保持し、それを更新したい」という場面に直面した場合、この制限が壁となります。ここで登場するのが mutable指定子 です。これを使うことで、ラムダ式の本体を一時的なオブジェクトのように扱い、キャプチャした変数を安全に書き換えることが可能になります。
基礎知識: ラムダ式とキャプチャの仕組み
ラムダ式は、定義された時点で内部的にクラス(関数オブジェクト)を生成します。値キャプチャ([x])を行うと、そのクラス内に変数 x のコピーがメンバ変数として保持されます。デフォルトでは、このラムダ式の「関数呼び出し演算子(operator())」には const が付与されているため、メンバ変数を変更することはできません。mutableを指定すると、このconst修飾が取り除かれ、キャプチャされた値を書き換えることが許可されるようになります。
実装/解決策: mutableの使い方
mutableを使用するには、ラムダ式の引数リストの直後(もし引数がない場合は括弧の後)に mutable と記述します。これにより、ラムダ式を呼び出すたびにキャプチャした変数の値を更新し、その結果を次の呼び出しに持ち越すことができます。
サンプルプログラム
以下のコードは、呼び出されるたびにカウンタをインクリメントするラムダ式の例です。
include
int main() {
int count = 0;
// mutableを指定することで、コピーされたcountを書き換え可能にする
auto increment = [count]() mutable {
count++; // ここで値を変更できる
return count;
};
std::cout << "1回目: " << increment() << std::endl; // 1 std::cout << "2回目: " << increment() << std::endl; // 2 std::cout << "3回目: " << increment() << std::endl; // 3 // 注意: 外側のcountは変更されていない std::cout << "元の変数の値: " << count << std::endl; // 0 return 0; }
応用・注意点: 現場での活用と落とし穴
mutableを使用する際の最大の注意点は、「元の変数は書き換わらない」という点です。ラムダ式内で変更されるのはあくまでキャプチャされた「コピー」です。もし元の変数を直接書き換えたい場合は、参照キャプチャ([&x])を使用するのが適切です。
また、mutableを使用したラムダ式は「状態を持つ」ため、並列処理(std::asyncやstd::threadなど)で同じラムダインスタンスを複数のスレッドから同時に呼び出すと、データ競合が発生する危険があります。スレッドセーフな設計が求められる現場では、std::atomicを使用するか、ミューテックスによる排他制御を検討するようにしましょう。適切に使えば、関数オブジェクトを手書きする手間を省ける非常に強力なツールとなります。

コメント