Как корутина может породить висячую ссылку и когда это стреляет?
danger принимает аргумент по ссылке и использует его после точки приостановки. Последующий resume() читает мусор, хотя код компилируется без ошибок.
Task<void> danger(const std::string& s) {
co_await something();
use(s);
}
void caller() {
auto t = danger("hello");
}
Найдите ошибку и объясните причину.
Параметр корутины по ссылке не копируется во фрейм — копируется лишь ссылка. Если объект вызывающего умирает до возобновления, его использование — UB. Стреляет после первой приостановки, когда стек вызывающего размотан.
- ✗Считать, что все параметры корутины глубоко копируются во фрейм, включая ссылки
- ✗Передать временный объект в параметр-ссылку корутины и использовать его после приостановки
- ✗Захватить ссылку на локальную вызывающего и возобновить корутину после его возврата
- →Почему передача параметра по значению делает фрагмент безопасным?
- →Как эта опасность сочетается с дефолтными аргументами, являющимися временными?
Висячая ссылка во фрейме
Task<void> danger(const std::string& s) { // s — ссылка, во фрейм НЕ копируется
co_await something(); // вызывающий уже размотал стек
use(s); // ❌ UB: referent уничтожен
}
void caller() {
auto t = danger("hello"); // временный std::string создан здесь...
} // ...и уничтожен в конце полного выражения
// корутина приостановлена; временный мёртв; последующий resume() — UB
Корутинный фрейм хранит локальные переменные, но параметр-ссылка хранит только ссылку. Временный std::string живёт до конца полного выражения вызова danger(...). Как только корутина приостановилась на co_await, этот временный уничтожается. Любой resume() после этого читает мёртвый объект.
Безопасно: по значению
Task<void> safe(std::string s) { // s КОПИРУЕТСЯ во фрейм
co_await something();
use(s); // ✅ OK: копия живёт во фрейме
}
Передача по значению кладёт собственную копию во фрейм; её время жизни совпадает с временем жизни фрейма. Правило: параметры корутины принимайте по значению, если только не можете доказать, что referent переживёт весь фрейм.