因为c++20协程是底层原语,标准库仅提供coroutine_handle、promise_type等基础组件,generator需手动实现迭代器协议、状态管理、异常传播及生命周期控制,稍有不慎就会导致编译失败、悬空指针或内存泄漏。

为什么不能直接用 std::coroutine_handle 写一个“开箱即用”的生成器?
因为 C++20 协程是底层原语,没有 generator<t></t> 这种标准类型——你得自己搭骨架。标准库只提供 std::coroutine_handle、promise_type 和挂起点(co_await、co_yield、co_return),所有状态管理、迭代器封装、异常传播都得手动处理。
常见错误是只实现 promise_type 但没配好 iterator,结果调用 for (auto x : gen) 编译失败;或者忘了在 ~generator() 中检查是否已 resume 完,导致悬空指针或 double destroy。
-
promise_type::get_return_object()必须返回generator<t></t>实例,且内部持有coroutine_handle<promise_type></promise_type> -
promise_type::yield_value(T&& v)要把值存进 promise 成员,并返回suspend_always{},否则co_yield不挂起 - 构造函数里不能直接
resume(),必须延迟到第一次begin()或operator++()时才启动协程
如何让 generator<int></int> 支持范围 for 循环?
关键不是协程本身,而是让 generator 满足 C++ 范围迭代器协议:提供 begin() / end(),且返回的迭代器支持 operator!=、operator++、operator*。
最简实现中,generator::iterator 只需保存一个 coroutine_handle<promise_type></promise_type>,每次 operator++() 调用 resume(),然后检查是否 done();operator* 直接读取 promise 里缓存的 current_value。
-
begin()必须触发首次 resume(跳过初始挂起),end()返回空 handle 或特殊标记 - 迭代器的
operator!=不能只比 handle 地址,要判断是否已结束(handle.done()) - 如果协程抛异常,
promise_type::unhandled_exception()必须捕获并 rethrow 到迭代器外,否则程序 abort
co_yield 的值怎么安全传给外部?
不能直接返回局部变量引用,也不能让多个 co_yield 共享同一块栈内存。标准做法是在 promise_type 里加一个 T current_value 成员,用 placement new 或 std::optional<t></t> 管理生命周期。
注意:如果 T 是非 trivial 类型,current_value 必须在 yield_value() 中显式构造,在 destroy() 或下次 yield 前显式析构,否则可能泄漏或重复析构。
- 推荐用
std::optional<t></t>:它自动处理构造/析构,且has_value()可作迭代器是否有效的判断依据 - 不要在
promise_type构造函数里初始化current_value,而应在yield_value()中调用emplace() -
iterator::operator*应返回std::as_const(current_value.value()),避免意外修改协程内部状态
实际写一个斐波那契生成器要注意什么?
示例函数 generator<long long> fib(int n)</long> 表面简单,但容易忽略边界和资源清理:
generator<long long> fib(int n) {
long long a = 0, b = 1;
if (n >= 1) co_yield a;
if (n >= 2) co_yield b;
for (int i = 3; i
<p>问题在于:如果用户只遍历前几个值就跳出循环(比如 <code>break</code>),协程不会自动销毁,<code>generator</code> 析构时必须调用 <code>handle.destroy()</code>,否则内存泄漏;但如果协程已运行结束,再调用 <code>destroy()</code> 会 UB。</p>
<ul>
<li>
<code>generator</code> 析构函数里必须先检查 <code>handle && !handle.done()</code>,才调用 <code>handle.destroy()</code>
</li>
<li>不要在 <code>fib()</code> 内部用 <code>static</code> 变量缓存状态,协程实例间必须完全隔离</li>
<li>若 <code>n</code> 为 0,<code>co_yield</code> 一次都不执行,此时 <code>begin() == end()</code>,迭代器应立即判定为结束</li>
</ul>
<p>协程生成器真正的复杂点不在语法,而在对象生命周期与控制流的耦合——resume、suspend、destroy 的时机必须和迭代器操作严格对齐,差一行代码就可能 crash 或静默错误。</p></long>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











