На cppreference я прочитал следующее (выделено мной):
Выход за конец сопрограммы эквивалентен co_return;, за исключением того, что поведение не определено, если в области видимости Promise не найдено ни одного объявления return_void.
Но clang 18.1.0 ведет себя по-другому в отношении уничтожения локальных переменных. Когда я падаю с конца сопрограммы, локальные переменные разрушаются, а затем вызывается return_value. Когда я использую co_return;, return_value вызывается перед уничтожением локальных переменных.
Мой вопрос: делает ли cppreference более сильное заявление о co_return; против падения, чем фактический стандарт? Является ли поведение лязгов ошибкой или соответствует стандарту?
Вот небольшой образец, показывающий разницу, а также крестик для вашего удобства:
#include <iostream>
#include <coroutine>
struct Coroutine {
struct promise_type {
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
Coroutine get_return_object() { return {}; }
void unhandled_exception(){}
void return_void() {
std::cout << "return_void called\n";
}
};
};
struct LogOnDestruct {
~LogOnDestruct() {
std::cout << "Destructed\n";
}
};
Coroutine foo() {
LogOnDestruct logger{};
co_await std::suspend_never{};
}
Coroutine bar() {
LogOnDestruct logger{};
co_return;
}
int main() {
foo();
bar();
return 0;
}





Стандарт довольно ясен:
Если
p.return_void()является допустимым выражением, выход из конца сопрограммы эквивалентенco_returnбез операнда; в противном случае выход за пределы сопрограммы приведет к неопределенному поведению.
Структура сопрограммы такова, что определяемое вами тело функции фактически находится внутри области действия более крупной функции, поэтому все локальные переменные этой функции находятся в этой области. Таким образом, co_return выходит из этой области, как если бы это был оператор goto. Но это происходит только после вызова соответствующей функции обещания. Таким образом, обещание должно получить вызов функции до того, как будут уничтожены любые локальные переменные.
Я правильно понимаю, что это будет дефектом реализации clangs? (например: правильное поведение — это явное поведение co_return;, поведение спада — это дефект лязга)
@wutz Ну, co_return expr; должно произойти до того, как локальные переменные будут уничтожены (я думаю: в противном случае висящие вещи в expr очень опасны), поэтому co_return; должно имитировать это, а бездействие должно имитировать co_return;.