Можно ли использовать циклический цикл С++ 11 с диапазоном rvalue-init?
Предположим, что у меня есть функция, которая возвращает значение std::vector
по значению:
std::vector<int> buildVector();
Казалось бы естественным перебирать результат с использованием диапазона for
:
for (int i : buildVector()) {
// ...
}
Вопрос: Безопасно ли это сделать?
Мое чтение стандарта (на самом деле, черновик n4431) предполагает, что это может быть не так, хотя мне тяжело полагая, что комитет не смог разрешить это использование. Я надеюсь, что мое чтение неверно.
В разделе 6.5.4 определяется диапазон for
:
for ( for-range-declaration : expression ) statement
со следующим обессериванием:
{
auto && __range = range-init;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}
где range-init
является просто ( expression )
и, по крайней мере, для типов классов, begin-expr
является либо __range.begin()
, либо begin(__range)
и т.д.
В моем примере buildVector
я думаю, что range-init
создает временное значение, которое реализация может быть уничтожена сразу после привязки ссылки __range
. Это означало бы, что ссылка __range
может уже свисать с временем begin-expr
.
Конечно, всегда должно быть безопасно написать это:
std::vector<int> notATemporary = buildVector();
for (int i : notATemporary) {
// ...
}
Но я надеюсь, что мне не нужно добавлять это в свой список gotchas.
Ответы
Ответ 1
Да, это совершенно безопасно.
Из [class.temporary]/4-5:
Есть два контекста, в которых временные объекты уничтожаются в другой точке, чем конец fullexpression. Первый контекст - это когда конструктор по умолчанию называется [...]
Второй контекст - это когда привязка привязана к временному. Временное, на которое ссылается связанный или временный, являющийся полным объектом подобъекта, к которому привязана ссылка , сохраняется для времени жизни ссылки, за исключением:
- Временная привязка к ссылочному элементу в конструкторах ctor-initializer [...]
- Временная привязка к ссылочному параметру в вызове функции [...]
- Время жизни временной привязки к возвращаемому значению в функции return statement [...]
- Временная привязка к ссылке в new-initializer [...]
Ни одно из этих исключений не применяется. Временное, таким образом, сохраняется для времени существования ссылки, __range
, который является целым циклом.