c++20通过co_yield实现惰性求值生成器,可按需产生数据;定义generator模板类并结合协程接口,用range函数示例展示逐值生成;通过next()和value()遍历输出1至5,适用于高效处理大量或无限序列。

C++20 引入了协程支持,使得我们可以用 co_yield 实现惰性求值的数据流生成器。这种生成器能按需产生数据,节省内存和计算资源,特别适合处理大量或无限序列的数据。
基本概念:协程与生成器
在 C++20 中,一个函数如果使用了 co_yield、co_await 或 co_return,就成为协程。要实现生成器(generator),我们需要定义一个返回类型,它满足协程的接口要求,并管理协程的生命周期。
标准库目前没有内置 generator 类型,但可以自己实现一个简单的版本,或者使用第三方库如 cppcoro。下面是一个基于 C++20 的简易 generator 实现。
手写一个简单的 generator 类
我们通过定义一个名为 generator 的模板类,配合协程特性来实现数据流生成。
立即学习“C++免费学习笔记(深入)”;
#include <coroutine>
#include <iostream>
template<typename T>
struct generator {
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
struct promise_type {
T value_;
bool done_ = false;
generator get_return_object() {
return generator(handle_type::from_promise(*this));
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
std::suspend_always yield_value(const T& value) {
value_ = value;
return {};
}
void unhandled_exception() { std::terminate(); }
};
handle_type h_;
explicit generator(handle_type h) : h_(h) {}
~generator() {
if (h_) h_.destroy();
}
generator(const generator&) = delete;
generator& operator=(const generator&) = delete;
generator(generator&& other) noexcept : h_(other.h_) {
other.h_ = nullptr;
}
generator& operator=(generator&& other) noexcept {
if (this != &other) {
if (h_) h_.destroy();
h_ = other.h_;
other.h_ = nullptr;
}
return *this;
}
bool next() {
if (!h_ || h_.done()) return false;
h_();
return !h_.done();
}
const T& value() const { return h_.promise().value_; }
};
使用 co_yield 编写数据流生成器函数
定义好 generator<t></t> 后,就可以编写返回该类型的函数,并在其中使用 co_yield 来逐步输出数据。
generator<int> range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i; // 每次调用 next() 时返回一个值
}
}
这个 range 函数不会一次性生成所有整数,而是每次迭代时才计算下一个值,实现了惰性求值。
如何使用生成器遍历数据
通过循环调用 next() 和访问 value(),可以逐个获取生成的元素。
int main() {
auto gen = range(1, 6);
while (gen.next()) {
std::cout << gen.value() << ' ';
}
std::cout << '\n'; // 输出: 1 2 3 4 5
return 0;
}
这段代码会依次打印从 1 到 5 的数字,每次调用 next() 触发一次协程执行,直到完成。
这种方式非常适合用于模拟无限序列、文件行读取、树遍历等场景,避免预加载全部数据。
基本上就这些。C++20 协程虽然语法稍复杂,但一旦掌握,就能写出高效、清晰的惰性数据流逻辑。注意编译时需启用 C++20 支持(如 GCC 加 -std=c++20),并且不同编译器对协程的支持程度可能略有差异。










