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>
ref
auto&& ref = *b;
*b
operator*
T
T
*b
auto&&
私は個人的に、範囲の有無にかかわらず、を多用しています。しかし、初期化子がxvalueであるかどうか、もしそうなら、参照されているものの存続期間はどれくらいかを毎回自問します。