Promise的final_suspend是否应始终返回std::suspend_always?
关于C++20协程
final_suspend的疑问与解析 在Josuttis所著的C++20书籍中,第14.2.5节第476页提到:
final_suspend用于定义协程是否应在最终阶段挂起。这里我们指定要执行挂起,这通常是正确的做法。注意该成员函数必须保证不抛出异常,它应始终返回std::suspend_always{}。
这段表述看起来有些矛盾——如果final_suspend确实“应始终返回std::suspend_always{}”,那标准为何不直接简化要求,让它返回void,由编译器自动插入std::suspend_always{}的逻辑?
事实上,该书后续第15.7.3节第536页就给出了反例:final_suspend返回了一个自定义的FinalAwaiter等待器,用于实现对称转移。这个等待器的await_ready和await_resume行为和std::suspend_always一致,但await_suspend逻辑不同,会返回目标协程句柄或空操作协程句柄:
struct FinalAwaiter { bool await_ready() noexcept { return false; } std::coroutine_handle<> await_suspend(CoroTask::CoroHdl h) noexcept { if (h.promise().contHdl) { return h.promise().contHdl; } else { return std::noop_coroutine(); } } void await_resume() noexcept {} };
回到最初的表述,它的“深意”在于针对绝大多数常规场景的简化指导:
- 对于普通的协程使用场景(比如简单的任务返回、生成器),
final_suspend返回std::suspend_always是最安全的选择:它确保协程结束时不会自动销毁自身,而是把销毁的控制权交给持有协程句柄的调用方,避免悬空引用或提前释放资源。 - 而返回自定义等待器是高级场景的特殊需求,比如对称转移、自定义资源清理逻辑等,这类场景属于“例外情况”,不是初学者或常规使用时需要考虑的。
Josuttis在前期章节先给出最通用、最安全的规则,是为了帮助读者先建立正确的基础认知,后续再逐步引入高级用法,符合循序渐进的教学逻辑。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

