在C++20里,std::generator(来自 <experimental/coroutine> 或 std::experimental::generator)提供了一个简单的协程包装器,允许你像写普通函数一样写“生成器”函数。下面给出一个完整的示例,演示如何编写一个斐波那契数列生成器,并在主程序中使用它。
#include <iostream>
#include <experimental/generator> // C++20
using namespace std::experimental; // std::generator
// 1. 斐波那契数列生成器
generator<unsigned long long> fib(unsigned int count) {
unsigned long long a = 0, b = 1;
for (unsigned int i = 0; i < count; ++i) {
co_yield a; // 暂停并返回当前值
std::tie(a, b) = std::make_pair(b, a + b);
}
}
// 2. 主程序
int main() {
const unsigned int N = 20;
std::cout << "前" << N << "个斐波那契数:\n";
for (auto n : fib(N)) {
std::cout << n << ' ';
}
std::cout << '\n';
return 0;
}
代码要点
-
协程函数
- `generator ` 是一个返回值类型,内部已经为我们实现了迭代器。
co_yield用来返回当前值,并把协程挂起。下次迭代时会从co_yield之后继续执行。
-
生成器使用
- 通过范围
for (auto n : fib(N))直接迭代生成器返回的值。 - 生成器内部管理协程状态、内存以及迭代器的推进,外部代码几乎不需要关心协程的细节。
- 通过范围
-
性能与内存
generator的实现通常基于std::coroutine_handle,只在第一次调用时分配一次堆内存。- 对于需要大量元素但仅按需使用的场景(如大文件行读取、懒加载序列等),
generator能显著减少一次性内存占用。
进阶:与 std::ranges 结合
C++20 的 ranges 也与协程配合得非常好。你可以直接使用 std::ranges::views::iota 来生成一个无限序列,然后用自定义过滤器:
#include <iostream>
#include <experimental/generator>
#include <ranges>
generator <int> filter_even(auto&& seq) {
for (int x : seq) {
if (x % 2 == 0)
co_yield x;
}
}
int main() {
auto evens = filter_even(std::views::iota(0) | std::views::take(20));
for (int v : evens) std::cout << v << ' ';
}
这样就能实现“生成所有偶数,限制前 20 个”的功能,完全借助协程和 ranges。
小结
std::generator是 C++20 对协程的简易封装,使用起来像普通迭代器。- 只需在协程体内使用
co_yield,剩余的状态管理交给标准库。 - 与
std::ranges搭配可编写更高级的流式数据处理。 - 对于需要延迟计算或生成无限序列的场景,它是一个既高效又简洁的工具。
如果你在项目中需要处理大型数据集或异步事件流,强烈建议尝试 std::generator,它能让代码既保持简洁,又获得协程带来的性能优势。