23

コーディング スタイルに関して、人々は C++0x ラムダをどのように使用しているのでしょうか。最も興味深い質問は、キャプチャ リストを作成する際にどの程度徹底するかということです。一方では、言語はキャプチャされた変数を明示的にリストすることを許可し、「明示的なルールは暗黙的なルールよりも優れている」ため、意図を明確に述べるために網羅的なリストを作成することは理にかなっています。例えば:

 int sum;
 std::for_each(xs.begin(), xs.end(), [&sum](int x) { sum += x });

これに対するもう 1 つの議論は、ref キャプチャされたローカルの有効期間は、キャプチャされたという理由だけで変更されないため (したがって、ラムダは、有効期間が長い間終了しているローカルを簡単に参照してしまう可能性があるため)、キャプチャを明示的にすると、そのようなバグを減らすのに役立つということです。そしてそれらを追跡します。

一方、この言語は、参照されているすべてのローカルを自動キャプチャするためのショートカットも意図的に提供しているため、明らかに使用することを意図しています。そして、上記のような例では、自動キャプチャでも何が起こるかは非常に明確であり、ラムダの寿命は周囲のスコープよりも長生きしないため、使用しない理由はないと主張できます:

 int sum;
 std::for_each(xs.begin(), xs.end(), [&](int x) { sum += x });

明らかに、これはオール オア ナッシングである必要はありませんが、いつ自動キャプチャするか、いつキャプチャを明示的に行うかを決定する何らかの論理的根拠が必要です。何かご意見は?

同様に、capture-by-copy -[=]をいつ使用するか、capture-by-reference - をいつ使用するかという問題もあり[&]ます。キャプチャによるコピーは、有効期間の問題がないため、明らかに安全です。そのため、キャプチャされた値を変更する必要がない場合 (または他の場所からの変更を確認する必要がない場合) は常にデフォルトで使用する必要があると主張できます。そのような場合、参照による参照は (時期尚早の可能性がある) 最適化として扱われ、明らかに違いが生じる場合にのみ適用されます。

一方、参照によるキャプチャはほとんどの場合高速です (特に、ほとんどの STL アルゴリズムなどの小さな型やインライン化可能なテンプレート関数の場合、コピーが実際に高速である場合は、コピーに最適化できることが多いため)、ラムダがそのスコープを超えない場合は安全です (これはすべての STL アルゴリズムの場合でもあります)。したがって、この場合の参照によるキャプチャをデフォルトにすることは、問題のない簡単で無害な最適化です。

あなたの考えは何ですか?

4

5 に答える 5

6

「暗黙のルールよりも明示的なルールの方が優れている」というルールは聞いたことがありませんし、同意しません。もちろん正しい場合もありますが、そうでない場合もたくさんあります。そういうわけで 0x はauto結局キーワードで型推論を追加しています。(そして、可能な場合に関数テンプレートのパラメーターが既に推論されている理由) 暗黙的が望ましい場合はたくさんあります。

私はまだ C++ ラムダを実際に使用したことはありませんが (VC10 ベータ版をいじる以外は)、ほとんどの場合は後者を使用します。

std::for_each(xs.begin(), xs.end(), [&](int x) { sum += x });

私の推論?なぜそれをしないのですか?便利です。できます。そして、それは維持するのがより簡単です。ラムダの本体を変更するときに、キャプチャ リストを更新する必要はありません。コンパイラが私よりもよく知っていることについて、なぜ私が明示しなければならないのでしょうか? コンパイラは、実際に使用されているものに基づいてキャプチャ リストを把握できます。

参照対値によるキャプチャは?通常の関数と同じルールを適用します。参照セマンティクスが必要な場合は、参照によってキャプチャします。コピーセマンティクスが必要な場合は、そうしてください。どちらでもよい場合は、小さい型の値を優先し、コピーが高価な場合は参照してください。

通常の関数を設計するときに行わなければならない選択と変わらないようです。

おそらくラムダの仕様を読む必要がありますが、一部の変数を値でキャプチャし、他の変数を参照でキャプチャできるようにするための明示的なキャプチャリストの主な理由ではありませんか?

于 2009-07-30T05:51:46.830 に答える
2

私の最初の本能は、値によるキャプチャーは Java の匿名内部クラス (既知の数量) とほぼ同じものを提供するというものでした。ただし、囲んでいるスコープを変更可能にする場合は、サイズ 1 の配列のトリックを使用するのではなく、代わりに参照によってキャプチャできます。その場合、ラムダの期間をリファランドの範囲内に制限するのはあなたの責任です。

実際には、アルゴリズムを扱うときは参照によるキャプチャをデフォルトにする必要があることに同意します。これが大部分の用途になると思います。Java での匿名内部クラスの一般的な用途はリスナーです。そもそも C++ で見られるリスナー スタイルのインターフェイスは少ないため、必要性は低くなりますが、それでも存在します。エラーの可能性を避けるために、そのような場合は厳密に値によるキャプチャーに固執するのが最善かもしれません。shared_ptr の値によるキャプチャーは、大きなイディオムになるのでしょうか?

ただし、ラムダはまだ使用していないため、何か大きなものを見逃している可能性があります。

于 2009-07-30T01:03:22.977 に答える
0

便利な場合は明示的なキャプチャリストを使用します。多くの変数をキャプチャする場合は(おそらく何か間違ったことをしているので)、すべての[&]キャプチャリストを取得することができます。

私の考えでは、明示的なキャプチャリストが理想的であり、暗黙的なバリアントは避けて、実際に必要なときに大量のコードを入力する必要がないようにする必要があります。

于 2009-08-02T09:40:53.370 に答える
0

ここで新しいコーディング標準ルールを見ることができます!;)

これは少し工夫されていますが、明示することの「利点」を強調するために、次のことを考慮してください。

void foo (std::vector<int> v, int x1)
{
  int sum = 0;
  std::for_each (v.begin ()
    , v.end ()
    , [&](int xl) { sum += x1; } 
}

さて、これにはわざと貧弱な名前などを選びましたが、それは要点を説明するためだけのものです。明示的なキャプチャリストを使用した場合、上記のコードはコンパイルされませんが、現在はコンパイルされます。

非常に厳しい環境(セーフティクリティカル)では、このようなルールがコーディング標準の一部であることがわかります。

于 2009-07-30T10:41:23.453 に答える
0

C ++ラムダをよりよく理解するために、次のリンクを読んでいます。例で使用されているコーディングスタイルは非常にきちんとしていて、私は次のようにフォローできます:http: //uint32t.blogspot.com/2009/05/using-c0x-lambda-to-replace-boost-bind.html

于 2009-08-03T05:24:49.417 に答える