#опытным
На самом деле у проблемы в этом коде:
Foo generateData() {
return Foo{std::vector{1, 2, 3, 4, 5}};
}
for (int x : generateData().items()) {
process(x);
}есть еще более простое решение.
Просто перейдите на С++23 и в коде не будет UB! Теперь время жизни всех временных объектов, которые нужны для получения итерируемой коллекции, продлеваются до конца цикла.
Стоит обратить внимание, что даже в C++23 параметры-не-ссылки промежуточных вызовов функций не получают продления времени жизни (поскольку в некоторых ABI они уничтожаются в вызываемой функции, а не в вызывающей), но это является проблемой только для функций, которые и так содержат ошибки:
using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t1) { return t1; } // всегда возвращает висячую ссылку
T g();
void foo()
{
for (auto e : f1(g())) {} // OK: время жизни возвращаемого значения g() продлено
for (auto e : f2(g())) {} // UB: локальный объект t1 функции f2 все равно разрушается при выходе из скоупа f2
}
Можно сказать, что продлевается жизнь только тех объектов, которые созданы в скоупе функции, содержащей сам цикл.
Вроде круто, но задумайтесь на секунду.
UB для обычного С++ программиста со стороны выглядит вот так: он меняет компилятор, какие-то флаги компилятора или компилирует на другой платформе и поведение программы меняется.
То есть с точки зрения стандарта UB ушло, но код меняет поведение в зависимости от флагов, что делает его менее предсказуемым и нужно знать все эти детали.
А как вы думаете: полезное изменение?
👍 если полезное, ☃️ если лучше бы оставили уб и не давали расслабиться программистам.
Solve problems. Stay cool.
#cpp23