在C++20中,协程为惰性计算提供了语言级支持。生成器(Generator)是一种典型应用:它能在每次被请求时产出一个值,而不需要一次性把所有结果放进容器。核心就在于co_yield表达式,它会把值返回给调用者,并挂起协程,等待下一次恢复。

什么是C++协程生成器
生成器是一个可迭代对象,表面上看像容器,但实际值在需要时才会计算。传统写法要用类手动保存状态,而C++协程借助编译器生成的状态机,让函数体内就能用co_yield产出序列。
协程的三个关键部分
- Promise类型:定义协程行为,如initial_suspend、yield_value等
- co_yield:挂起协程并返回一个值
- 句柄std::coroutine_handle:用于恢复或销毁协程
实现一个简单的整数生成器
下面给出一个最小可用的Generator实现,可以逐个产出从start开始的n个整数。
#include <coroutine>
#include <iostream>
// 生成器模板
template<typename T>
struct Generator {
struct Promise {
T value;
std::suspend_always yield_value(T v) {
value = v;
return {};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Generator get_return_object() {
return Generator{std::coroutine_handle<Promise>::from_promise(*this)};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
using promise_type = Promise;
std::coroutine_handle<Promise> h;
explicit Generator(std::coroutine_handle<Promise> h_) : h(h_) {}
~Generator() { if (h) h.destroy(); }
// 迭代器
struct Iterator {
std::coroutine_handle<Promise> h;
bool done;
Iterator(std::coroutine_handle<Promise> h_, bool d) : h(h_), done(d) {}
T operator*() const { return h.promise().value; }
Iterator& operator++() {
h.resume();
done = h.done();
return *this;
}
bool operator!=(const Iterator& o) const { return done != o.done; }
};
Iterator begin() {
h.resume();
return Iterator{h, h.done()};
}
Iterator end() { return Iterator{h, true}; }
};
// 协程函数
Generator<int> count_from(int start, int n) {
for (int i = 0; i < n; ++i) {
co_yield start + i;
}
}
int main() {
for (int v : count_from(3, 5)) {
std::cout << v << " ";
}
std::cout << std::endl;
return 0;
}
代码要点说明
Promise里的yield_value在co_yield时调用,把值存起来并挂起。迭代器在operator++中调用resume恢复协程,直到协程结束。注意Generator析构时必须destroy句柄,否则会内存泄漏。
co_yield的使用限制
co_yield只能出现在被编译器识别为协程的函数中,也就是函数里至少用了co_yield、co_await或co_return之一。返回类型不必是Generator,但必须能通过promise_type拿到可恢复的句柄。
如果协程里抛异常且未处理,默认示例直接terminate,实际项目可改为存异常并在迭代时重抛。
小结
利用C++协程实现生成器并不复杂:定义好Promise与返回对象,用co_yield产出值,再用迭代器包装句柄即可。这种方式比手写状态机更直观,也更容易维护。