C++协程中co_return与掉出末尾的行为差异:标准与Clang实现疑问
协程末尾与
co_return的行为差异分析 核心结论
cppreference的表述确实比C++标准更强,Clang 18.1.0的行为完全符合标准,并非bug。
标准与cppreference的表述差异
cppreference的简化描述:
掉出协程末尾等效于
co_return;,但如果Promise作用域内找不到return_void声明,行为未定义。
但C标准(C20及后续版本)对两者的执行顺序有明确区分:
- 当协程正常掉出末尾时:先销毁所有局部变量,再调用promise的
return_void()(若该成员存在)。 - 当执行
co_return;时:先调用promise的return_void(),再销毁所有局部变量。
cppreference的“等效”表述忽略了析构顺序的差异,属于过度简化的强表述,标准并未规定两者在析构与return_void的调用顺序上完全一致。
Clang行为的合规性
Clang的实现严格遵循标准:
- 对于掉出协程末尾的
foo()函数,先执行局部变量logger的析构,再调用return_void()。 - 对于使用
co_return;的bar()函数,先调用return_void(),再执行局部变量的析构。
这种行为完全符合标准对两种场景的执行顺序规定,不存在bug。
示例代码
#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; }
运行输出(Clang 18.1.0)
Destructed return_void called return_void called Destructed
内容的提问来源于stack exchange,提问作者Wutz
相关产品推荐
相关产品推荐

