2016-05-31 11 views
0

私はcppには新しく、ラムダ関数で作業しようとしています。私は、ラムダ関数の内部とその外側でインクリメントしようとしているカウンタを持っています。私は理解できない奇妙なメモリエラーを見ている。ここで私はこのカウンターで作業している流れです。私が間違っていることは何かありますか?C++ Lambda関数の終了 - メモリの問題

bool SomeClass::func() { 
    int64_t counter = 0; 

    //some loop logic { 
     counter++; 
    } 


    auto lamb = [this, &counter]() { 
     //some logic 
     counter++; 
    } 

    someotherFunction(data, lamb); // this function will execute the lambda 

} 
+3

'関数が' someotherFunction'の中で実行されると、 'counter'は依然としてスコープ内にあるので、この関数は*働かなければなりません。しかし、 'someotherFunction'がラムダを*格納していて、後で' func'が返った後にラムダが実行されると、参照問題が発生します。 –

+0

しかし、以下のコメントを見てみると、someotherFunctionの実行がfuncの実行を上回ると、それは正しく失敗するようです。 – sublime

+0

ポイントは*内部* 'func'から' someotherFunction'を呼び出していることです。つまり、 'func'よりも長くはできません。 –

答えて

7

問題は、それがどこかに保存されているため、ローカル変数は、ローカルの「所有者」(生存ラムダによって参照により捕捉された場合、すなわち、C++ラムダ「はupwards funarg」事件を解決することができないということですまたは関数の結果として返されたため)。

ラムダが参照によって変数をキャプチャするとき、C++はラムダコードによって使用されるコンテキスト構造に参照される変数のアドレスを格納するだけです。

ただし、変数自体はローカルであり、ラムダが作成されたスタックフレームに存在します。ラムダオブジェクトの実行中にラムダオブジェクトが呼び出されただけの場合は、問題はありませんが、ラムダがの代わりに格納されている場合は、となり、ラムダで参照されたキャプチャされた変数)が実行されると、それ以上存在しない変数を参照します(未定義の動作)。

一般的なケースでは "上方へのfunarg"問題を解決するにはガベージコレクタが必要であり、C++にはガベージコレクタが必要です。あなたはいくつかのケースで行うことができます「の値によって」キャプチャされているので、ラムダは、独自のプライベートコピーを持っています

:あなたが捕獲さを変更したいがあれば

この場合
foo([counter]() mutable { counter++; }) 

はあなたにも必要なコピーmutableというキーワードを使用するのは...キャプチャされたコピーを変更する場合はC++に必要とされるからです(キャプチャされたコピーはラムダの本体にあるconstのオブジェクトです)。

残念ながら、キャプチャされた変数を2つのラムダ(たとえば、同じキャプチャされた変数に「インクリメンタ」と「デクリメンタ」の両方を作成するなど)で共有する必要がある場合、コピーの使用は実行できません。 これでできることは、値によってstd::shared_ptrを変数に取り込むことです。ただし、単純なケースではガベージコレクタが正しく置き換えられます(ただし、参照ループの場合は使用されません)。

+0

この問題を回避するにはどうすればよいですか?ローカル変数をラムダに渡す方法はありますか? – sublime

+0

共有ポインタを使うと言ったとき、 'auto pin = std :: make_shared (counter);'と 'pin'を値で捕捉していますか? @ 6502 – Anzurio

+1

@Anzurio:はい。ラムダライフタイム自体が、キャプチャされたオブジェクトとの参照のループを潜在的に作成する可能性のある共有ポインタによって処理されていない場合(C++はガベージコレクタを提供しないため、リファインされたrefcountedループが廃棄されます) – 6502

0

6502の優秀な答えに実用的なアプローチの追加:ここで

auto make_counter(int initial) { 
    return [initial] (void) mutable { 
    return ++initial; 
    }; 
} 

を、ローカル変数は(実際には関数のパラメータですが、それは重要ではありません)」、それのコピーがありますを意味し、値によって捕獲されますその「ラムダ」のすべてのオブジェクトの「内部」にある。カウンタをインクリメントできるようにするために、ラムダはキャプチャされた変数を変更できるようにする必要があります。つまり、mutableです。