co_yield 是协程挂起点而非返回语句,它暂停执行并交出值,保留局部变量和栈帧以支持状态机式数据流;必须在含 promise_type 的协程函数中使用,且 yield_value 等接口缺一不可。

co_yield 是协程挂起点,不是返回值语句
它让协程暂停执行并把值“交出去”,但不结束协程——下次恢复时从 co_yield 后面继续。这和 return 或普通函数返回有本质区别:协程状态(局部变量、栈帧)被保留,适合做状态机式的数据流生成。
常见错误是把它当 yield(Python)或 yield return(C#)来用,结果发现编译不过或行为异常——C++ 要求函数声明为协程,必须有 promise_type,且返回类型得支持 co_yield 操作。
- 必须在返回类型含
promise_type的协程函数里使用,比如自定义的generator<t></t> - 不能在普通函数、lambda(除非显式标注
operator co_await)、或未实现yield_value的 promise 里用 - 每次
co_yield都会调用 promise 的yield_value,你得自己处理值的存储与传递逻辑
实现 generator 必须补全 promise_type 接口
标准库没提供 generator,得手写。核心是让返回类型(如 generator<int></int>)内部嵌套的 promise_type 实现 get_return_object、initial_suspend、final_suspend、unhandled_exception 和 yield_value —— 少一个,co_yield 就报错。
典型错误现象:error: use of undeclared identifier 'co_yield' 或 no member named 'yield_value' in 'generator<int>::promise_type'</int>,往往是因为漏了 yield_value 或返回对象构造不对。
立即学习“C++免费学习笔记(深入)”;
-
yield_value(T&& v)必须返回std::suspend_always或std::suspend_never,决定是否挂起 -
get_return_object()返回的是协程 handle 可操作的对象(比如含iterator的generator实例) -
initial_suspend设为std::suspend_always,避免协程一启动就跑完;final_suspend也建议设为std::suspend_always,方便外部控制生命周期
无限序列生成器的关键:用 co_yield + while(true) 控制流
无限序列不是靠“多分配内存”实现的,而是靠协程每次只算一个值、挂起、等下次请求再算下一个。内存占用恒定,和序列长度无关。
容易踩的坑是把计算逻辑写在协程外,或者在 co_yield 后没更新状态变量,导致反复 yield 同一个值。比如想生成斐波那契,却忘了在 co_yield 后交换 a 和 b。
- 状态变量(如计数器、上一项值)必须是协程函数的局部变量,才能跨挂起保留
- 别在循环里直接
co_yield i++,而应先算值再co_yield,否则可能因挂起时机导致副作用顺序错乱 - 示例片段:
generator<int> ints_from(int start) { for (int i = start;; ++i) { co_yield i; // 每次挂起,下次从 ++i 开始 } }
迭代 generator 时要注意 operator++ 的 suspend 行为
generator 对象本身不保存迭代位置,它的 iterator 类型才管这个。每次 ++it 实际触发协程 resume,执行到下一个 co_yield 或结束。如果协程已结束,it == end()。
常见问题:循环中意外提前退出,或卡死——多半因为没检查 it != gen.end() 就解引用;或者协程抛异常但 unhandled_exception 没处理,导致未定义行为。
- 务必用
for (auto x : gen)或显式比较it != gen.end(),不能假设generator支持随机访问 - 若生成逻辑可能失败(比如 I/O、分配失败),
unhandled_exception必须 rethrow 或记录,否则崩溃无提示 - 性能上,每次
++it有 resume/suspend 开销,比数组遍历慢,但换来的是零内存预分配










