2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[C++] ラムダ式でハマった話

Last updated at Posted at 2023-12-26

はじめに

C++ でラムダ式を書いていたらハマったのでメモ.

単刀直入に言うと,局所変数の参照をラムダ式のキャプチャリストに書くな というだけ.

症状

C++ で書いた実行ファイルを,コマンドラインから実行した場合と,Python の subprocess から実行した場合で結果が異なるという現象に遭遇した.

さらに,Debug ビルドと Release ビルドでも結果が異なった.

何事かと思ったら,ラムダ式に問題があった.

原因

ラムダ式を使用する部分で,こんなコードがあった.

std::function<int(int)> f;
if (foo) {
    int bar = /* some value */ 0;
    f = [&](int a) {
        // bar を使った処理
        return a + bar;
    };
}

if ブロックの中でローカル変数 bar を宣言したあと,キャプチャリストに参照 [&] を指定してからラムダ式内部で bar を使用している.

当然,if ブロックを抜けた後は,変数 bar にはアクセスできなくなるので,ラムダ式実行時に bar の参照が示す値は未定義となる.

変数 bar があった場所の値をいつ書き換えるかは OS 次第なので,Debug 時は動作していても Release ビルドすると不具合になったり,他のプロセスから実行すると結果が変わったり,というような再現性のないことになる.

警告も何も出してくれないので要注意.

対処法

キャプチャリストを参照ではなくコピーにするだけ.

f = [=](int a) { // [&] ではなく [=] とする
    return a + bar;
};

[bar] と書いても良いし,他の変数は参照でキャプチャしたい場合は [&, bar] とも書ける.std::vector など比較的大きいデータの場合は,なるべく参照でキャプチャしたい.

関連

局所変数の参照という点に関連して,よく問題になるコードがこれ.

int* func() {
    int v = 0;
    return &v; // ダメ
}

ローカル変数のポインタを返すな,というもの.
こちらの方がいささか有名だが,セットで押さえておきたい.

おわりに

C++ に慣れている人にとっては常識かもしれませんが,自戒のため記事にしました.
ラムダ式をあまり使わない人や,脳死で [&] を書いてしまう人は留意してください.

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?