範囲ベースのforループで転送参照を使用する利点は何ですか?


115

const auto&読み取り専用の操作を実行する場合は十分です。しかし、私はぶつかった

for (auto&& e : v)  // v is non-const

最近数回。これは私に不思議に思います:

またはと比較して、一部のあいまいなケースで、転送参照を使用することでパフォーマンスが向上する可能性はありますauto&const auto&

shared_ptrあいまいなコーナーケースの容疑者です)


更新 お気に入りで見つけた2つの例を。

基本型を反復するときにconst参照を使用することの欠点はありますか?
範囲ベースのforループを使用して、マップの値を簡単に反復できますか?

質問に集中してください:範囲ベースのforループでauto &&を使用する理由は何ですか?


4
あなたは本当にそれを「しばしば」見ますか?
オービットのライトネスレース

2
あなたの質問に、あなたがそれを目にしている場所がどれほど「クレイジー」であるかを判断するのに十分なコンテキストがあるかどうかはわかりません。
オービットのライトネスレース

2
@LightnessRacesinOrbit短い話:なぜauto&&範囲ベースのforループで使用したいのですか?
Ali

回答:


94

私が見ることができる唯一の利点は、シーケンス反復子がプロキシ参照を返し、その参照を非constな方法で操作する必要がある場合です。たとえば、次のことを考慮してください。

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto& e : v)
        e = true;
}

vector<bool>::referenceから返された右辺iterator値が非const左辺値参照にバインドされないため、これはコンパイルされません。しかし、これはうまくいきます:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto&& e : v)
        e = true;
}

そうは言っても、このようなユースケースを満たす必要があることを理解していない限り、この方法でコーディングすることはありません。つまり、あなたが今何をしているのだろうと人々に思わせるので、私はこれを不当に行うことはしません。そして、私がそれをしたとしても、理由についてコメントを含めることは害にはならないでしょう:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    // using auto&& so that I can handle the rvalue reference
    //   returned for the vector<bool> case
    for (auto&& e : v)
        e = true;
}

編集する

私のこの最後のケースは、本当に理にかなったテンプレートであるべきです。ループが常にプロキシ参照を処理していることがわかっている場合は、autoと同様に機能しauto&&ます。しかし、ループが非プロキシ参照とプロキシ参照を時々処理していたとき、私auto&&は選択の解決策になると思います。


3
一方、明確な欠点はありませんか?(混乱する可能性のある人々は別として、個人的には言及する価値はあまりないと思います。)
ildjarn '29

7
人々を不必要に混乱させるコードを書くためのもう1つの用語は、混乱したコードを書くことです。コードをできるだけ単純にするのが最善ですが、単純にすることはできません。これにより、バグの数を減らすことができます。そうは言っても、&&がより身近になるにつれて、おそらく5年後には、自動&&のイディオム(実際には害がないと仮定)が期待されるようになるでしょう。それが起こるかどうかはわかりません。しかし、単純なことは見る人の目にあります、そしてあなたがあなた自身より多くのために書いているなら、あなたの読者を考慮に入れてください。
ハワードヒナント2012年

7
const auto&シーケンス内の要素を誤って変更していないことをコンパイラーが確認できるようにしたい場合に、私は好みます。
ハワードHinnant

31
個人auto&&的には、シーケンスの要素を変更する必要がある一般的なコードで使用するのが好きです。そうでない場合は、に固執しauto const&ます。
Xeo

7
@Xeo:+1 C ++が進化し続けるのは、あなたのような愛好家が絶えず実験を行い、より良い方法を求めているからです。ありがとうございました。:-)
ハワードヒナン

26

範囲ベースのループを使用した、auto&&またはユニバーサル参照を使用すると、for取得した内容をキャプチャできるという利点があります。ほとんどの種類のイテレータでは、おそらく何らかのタイプのa T&またはのいずれかを取得します。興味深いケースは、イテレーターの逆参照が一時的なものを生成する場合です。C++ 2011では要件が緩和され、イテレーターは必ずしも左辺値を生成する必要はありません。ユニバーサル参照の使用は、次の引数の転送と一致します。T const&Tstd::for_each()

template <typename InIt, typename F>
F std::for_each(InIt it, InIt end, F f) {
    for (; it != end; ++it) {
        f(*it); // <---------------------- here
    }
    return f;
}

関数オブジェクトをf扱うことができT&T const&Tは異なります。範囲ベースのforループの本体が異なるのはなぜですか?もちろん、普遍的な参照を使用して型を推定したことを実際に利用するには、それに応じてそれらを渡す必要があります。

for (auto&& x: range) {
    f(std::forward<decltype(x)>(x));
}

もちろん、使用とstd::forward()は、移動元の戻り値を受け入れることを意味します。このようなオブジェクトが(まだ?)わからない非テンプレートコードで意味があるかどうか。ユニバーサル参照を使用すると、正しいことを行うためにコンパイラーにより多くの情報を提供できると想像できます。テンプレート化されたコードでは、オブジェクトで何が発生するかを決定することはできません。


9

私は事実上常に使用しますauto&&。あなたがする必要がないのに、なぜエッジケースに噛まれるのですか?タイプする方が短く、私はそれをもっと...透明だと思っています。を使用するauto&& xと、毎回x正確にがわかります*it


29
私の問題は、あなたが十分constauto&&あれconst auto&ばif をあきらめることです。質問は私がかまれることができるコーナーケースを尋ねます。ディートマーまたはハワードによってまだ言及されていないコーナーケースは何ですか?
Ali

2
ここでは、場合かま取得するための方法だやる使用がauto&&。キャプチャする型をループの本体で移動する必要がある場合(たとえば)、それが後の日付に変更されてconst&型に解決される場合、コードは暗黙のうちに機能し続けますが、移動はコピーになります。このコードは非常に不正です。ただし、タイプをr値参照として明示的に指定した場合、これらのオブジェクトを実際に移動してコピーしないようにしたいため、コンテナタイプを変更した場合はコンパイルエラーが発生します...
cyberbisson

@cyberbisson私はこれに遭遇しました:タイプを明示的に指定せずに右辺値参照を強制するにはどうすればよいですか?のようなものfor (std::decay_t<decltype(*v.begin())>&& e: v)?もっと良い方法があると思います...
Jerry Ma

@JerryMaそれはあなたタイプについて知っていることによって異なります。上記のように、auto&&「ユニバーサルリファレンス」を提供するため、それ以外の場合はより具体的なタイプが得られます。vがであると想定されている場合vector、あなたはそうすることができますdecltype(v)::value_type&&。これはoperator*、イテレータタイプの結果を取得することで、私が想定していることです。decltype(begin(v))::value_type&&コンテナの代わりにイテレータのタイプをチェックすることもできます。ただし、タイプに対する洞察力がほとんどない場合は、そのままにしておくと少し明確になると考えられauto&&ます...
。– cyberbisson
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.