Ответ 1
Во-первых, некоторые общие рекомендации о том, как использовать auto
, который не является специфичным для range-for. auto&&
может быть проблематичным, если инициализатор представляет собой значение x, относящееся к временному, поскольку расширение жизни в этом случае не может быть применено. Проще говоря, и с кодом:
// 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)
будет значением prvalue, а возвращаемое временное время будет расширенным (хотя это будет связано с конструкцией).
С учетом этого, когда дело доходит до диапазона, хотя вы должны помнить, что 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&&
, как в случае, например, с std::move_iterator<Iterator>
)? Затем он должен ссылаться на объект, который переживет ref
, так как строка auto&& ref = *b;
не содержит временных. Следовательно, это безопасно. В противном случае, если *b
является prvalue (т.е. Тип итератора имеет operator*
, возвращающий T
для некоторого типа объекта T
), тогда время жизни временного расширения распространяется на остальную часть тела цикла. Во всех случаях вы в безопасности (случай, когда *b
является lvalue, оставленным в качестве упражнения для читателя).
Я лично сильно использую auto&&
, с диапазоном или без него. Но я спрашиваю себя каждый раз, является ли инициализатор значением xvalue или нет, и если да, то каково время жизни того, на что ссылается.