首先,关于如何使用它的一些一般建议auto
并不特定于 range-for。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)
它将是一个纯右值,并且返回的临时值将延长其生命周期(但这将涉及构造)。
顺便说一句,当涉及到 range-for 时,尽管您必须记住它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*
返回value_type&&
,例如 with 的情况std::move_iterator<Iterator>
)呢?然后它必须引用一个比它更长寿的对象,因为ref
该行auto&& ref = *b;
不涉及临时性。因此它是安全的。否则,如果*b
是纯右值(即迭代器类型具有operator*
返回T
某个对象类型T
),那么临时的生命周期将延长到循环体的其余部分。在所有情况下,您都是安全的(将*b
左值作为练习留给读者的情况)。
我个人大量使用auto&&
, 有或没有 range-for。但是我每次都会问自己初始化器是否是一个 xvalue,如果是,所引用的生命周期是多少。