This question mentioned the obvious, idiomatic usage of C++11 range-based for.
for (auto& elem: container) {
// do something with elem
}
First, some general advice on how to use auto
that is not specific to range-for. auto&&
can be problematic if the initializer is an xvalue referring to a temporary, since lifetime extension may not be applied in this case. To put it more simply, and with code:
// 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 {});
The big clue that there's something shady going on here is that id
has return type T&&
. If it returned T
then id(whatever)
would be a prvalue, and the returned temporary would have had its lifetime extended (however that would involve a construction).
With that out of the way, when it comes to range-for though you have to remember that for(auto&& ref: init) { /* body */ }
is specified to be roughly equivalent to the following (ignoring some details that don't matter here):
{
using std::begin;
using std::end;
auto&& range = init;
for(auto b = begin(range), e = end(range); b != e; ++b) {
auto&& ref = *b;
/* body */
}
}
We need to ask ourselves now, what if *b
is an xvalue (i.e. the iterator type has an operator*
returning value_type&&
, as is the case e.g. with std::move_iterator<Iterator>
)? It must then refer to an object that will outlive ref
, since the line auto&& ref = *b;
involves no temporary. Hence it's safe. Otherwise, if *b
is a prvalue (i.e. the iterator type has an operator*
returning T
for some object type T
), then the lifetime of the temporary gets extended for the rest of the loop body. In all cases you're safe (the case where *b
is an lvalue being left as an exercise to the reader).
I personally make heavy use of auto&&
, with or without range-for. But I do ask myself every time whether the initializer is an xvalue or not, and if it is, what is the lifetime of what is being referred to.