12

この質問では、C++11の範囲ベースの明白で慣用的な使用法について説明しました。

for (auto& elem: container) {
  // do something with elem
}

しかし、私はあなたが使用することになっている参照の種類について疑問を持っていました。入力イテレータは右辺値を返す場合があります。によって導入された暗黙の型は、右辺値にバインドautoされると推測できますがconst、それは発生しないようです。

完全な転送を使用するための最良の一般的な方法はありますか?

for (auto && elem: container) {
  // do something with elem
}

ここにはマイナス面はありませんが、少し可愛すぎます。たぶん私はまだ十分なC++11を書いていません。

4

1 に答える 1

7

autoまず、範囲に固有ではない使用方法に関するいくつかの一般的なアドバイス。auto&&初期化子が一時的なものを参照するxvalueである場合、ライフタイム延長が適用されない可能性があるため、問題が発生する可能性があります。もっと簡単に言えば、コードを使って:

// Pass-through identity function that doesn't construct objects
template<typename T>
T&&
id(T&& t)
{ return std::forward<T>(t); }

// Ok, lifetime extended
// T {} is a prvalue
auto&& i = T {};

T* address = &i;

// Still ok: lifetime of the object referred to by i exceed that of j
// id(whatever) is an xvalue
auto&& j = id(std::move(i));

// No other object is involved or were constructed,
// all those references are bound to the same object
assert( &j == address );

// Oops, temporary expires at semi-colon
// id(whatever) is an xvalue, again
auto&& k = id(T {});

ここで何か怪しげなことが起こっているという大きな手がかりは、idリターンタイプがあることT&&です。Tそれが戻った場合、それはid(whatever)優先順位になり、返された一時的なものはその寿命が延長されたでしょう(ただし、それは建設を伴います)。


それが邪魔にならないように、範囲に関しては-覚えておく必要がありますが、それfor(auto&& ref: init) { /* body */ }は以下とほぼ同等に指定されていることを覚えておく必要があります(ここで重要ではないいくつかの詳細を無視します):

{
    using std::begin;
    using std::end;
    auto&& range = init;
    for(auto b = begin(range), e = end(range); b != e; ++b) {
        auto&& ref = *b;
        /* body */
    }
}

*bここで、xvalueが(たとえば、の場合のように、イテレータ型にoperator*戻り値がある)場合はどうなるかを自問する必要があります。この行には一時的なものが含まれていないため、長持ちするオブジェクトを参照する必要があります。したがって、それは安全です。それ以外の場合、がprvalueである場合(つまり、イテレータ型が何らかのオブジェクト型に対して戻り値を持っている場合)、一時的なものの存続期間はループ本体の残りの部分で延長されます。すべての場合において、あなたは安全です(読者への演習として左辺値が残されている場合)。value_type&&std::move_iterator<Iterator>refauto&& ref = *b;*boperator*TT*b

auto&&私は個人的に、範囲の有無にかかわらず、を多用しています。しかし、初期化子がxvalueであるかどうか、もしそうなら、参照されているものの存続期間はどれくらいかを毎回自問します。

于 2012-04-03T15:50:31.570 に答える