14

2 つのローカル スマート ポインターがあるfooとしbarます。

shared_ptr<Foo> foo = ...
shared_ptr<Bar> bar = ...

これらのスマート ポインタは、何らかの理由でfoo、次にの順に破棄する必要があるリソースのラッパーbarです。

fooここで、 andを使用するラムダを作成したいと思いますbarが、それらを含むスコープよりも長生きします。したがって、次のように値でキャプチャします。

auto lambda = [foo, bar]() { ... };

fooこれにより、bar関数オブジェクト内のコピーが作成されます。関数オブジェクトが破棄されると、これらのコピーも破棄されますが、これが発生する順序が気になります。だから私の質問は:

ラムダ オブジェクトが破棄されるとき、その値によるキャプチャはどのような順序で破棄されますか? そして、どうすれば(できれば)この順序に影響を与えることができますか?

4

4 に答える 4

19

仕様はこれをカバーしています...一種の。5.1.2、パラグラフ14から:

エンティティが暗黙的にキャプチャされ、capture-default が = である場合、または & を含まないキャプチャで明示的にキャプチャされた場合、エンティティはコピーによってキャプチャされます。コピーによってキャプチャされたエンティティごとに、名前のない非静的データ メンバーがクロージャー型で宣言されます。これらのメンバーの宣言順序は規定されていません。

強調が追加されました。宣言の順序が指定されていないため、作成の順序は指定されていません (作成の順序は宣言の順序と同じであるため)。したがって、破壊の順序は構築の順序の逆であるため、破壊の順序は指定されていません。

要するに、宣言の順序 (およびそれをキーオフするさまざまな構築/破棄の順序) を気にする必要がある場合は、ラムダを使用できません。独自のタイプを作成する必要があります。

于 2012-09-20T20:57:02.067 に答える
10

ニコルの言う通り、破壊の順番は不特定。

ただし、ラムダの破棄に依存する必要はありません。fooラムダの終わりに簡単にリセットできるはずです。これにより、リソースが解放される前に確実に解放されますbar。また、ラムダを同様にマークする必要がmutableあります。ここでの唯一の欠点は、ラムダを複数回呼び出して、それが機能することを期待できないことです。

auto lambda = [foo, bar]() mutable { ...; foo.reset(); };

ラムダを複数回呼び出し可能にする必要がある場合は、割り当て解除の順序を制御する別の方法を考え出す必要があります。1 つのオプションは、次のような既知のデータ メンバー順序を持つ中間構造体を使用することstd::pair<>です。

auto p = std::make_pair(bar, foo);
auto lambda = [p]() { auto foo = p.second, bar = p.first; ... };
于 2012-09-20T21:33:27.163 に答える
8

破壊の順序がどうなるかを心配するよりも、これが問題であるという事実を修正する必要があります。両方のオブジェクトに共有ポインターを使用していることに注意してください。オブジェクトに共有ポインターを追加することで、破棄の順序を保証できます。その時点で、またはが先に破壊されるかどうfoobarは問題ではありません。順序が正しければ、共有ポインターが破棄されると、オブジェクトはすぐに解放されます。順序が正しくない場合、追加の共有ポインターは、もう一方がなくなるまでオブジェクトを存続させます。

于 2012-09-20T21:31:17.770 に答える
5

私が持っている C++11 ドキュメント (つまり、景品、批准の少し前の n3242)、セクション 5.1.2、パラ 21 によると、キャプチャは宣言順に構築され、逆の宣言順で破棄されます。ただし、宣言の順序は規定されていません(パラグラフ 14)。したがって、答えは「不特定の順序で」、「影響を与えることはできません」です(コンパイラを書くことを除いて)。

bar を foo の前に破棄する必要がある場合は、bar が foo (またはそのようなもの) への共有ポインターを保持することが賢明です。

于 2012-09-20T21:00:12.030 に答える