C++20中的协程(Coroutines)如何使用?(入门代码示例)

C++20协程是可挂起恢复的函数,需手动实现promise_type、awaiter和返回类型三要素;示例Generator通过co_yield实现懒加载整数生成,强调initial_suspend、final_suspend及生命周期管理。

协程不是线程,也不启动新执行流,它是一种可挂起、可恢复的函数,用于简化异步逻辑、生成器、状态机等场景。C++20 协程是语言级支持,但需要手动实现协程桩(promise type)、awaiter 和返回类型——不依赖运行时库,但也意味着不能直接“开箱即用”。

基础结构:三要素缺一不可

每个协程必须有:

  • 返回类型:需定义 promise_type 嵌套类,并提供 get_return_object()initial_suspend()final_suspend()return_void()(或 return_value())等接口;
  • 挂起点:使用 co_await 表达式,其操作数需满足 awaiter 要求(含 await_ready()await_suspend()await_resume());
  • 挂起关键词co_return(结束协程)、co_await(挂起等待)、co_yield(挂起并产出值,常用于 generator)。

最简可运行示例:一个懒加载的整数生成器

下面是一个能逐次产出 0、1、2 的协程 generator(类似 Python 的 yield):

// 编译需支持 C++20:g++-11+ / clang++-13+,加 -std=c++20
#include 
#include 
#include 

template
struct Generator {
  struct promise_type;
  using handle_type = std::coroutine_handle

  struct promise_type {
    T current_value;
    auto get_return_object() { return Generator{handle_type::from_promise(*this)}; }
    auto initial_suspend() { return std::suspend_always{}; }
    auto final_suspend() noexcept { return std::suspend_always{}; }
    void return_void() {}
    void unhandled_exception() { std::terminate(); }
    auto yield_value(T value) {
      current_value = value;
      return std::suspend_always{};
    }
  };

  explicit Generator(handle_type h) : coro_(h) {}
  Generator(const Generator&) = delete;
  Generator& operator=(const Generator&) = delete;
  Generator(Generator&& rhs) noexcept : coro_(rhs.coro_) { rhs.coro_ = nullptr; }
  Generator& operator=(Generator&& rhs) noexcept {
    if (this != &rhs) {
      if (coro_) coro_.destroy();
      coro_ = rhs.coro_;
      rhs.coro_ = nullptr;
    }
    return *this;
  }
  ~Generator() { if (coro_) coro_.destroy(); }

  bool next() {
    if (!coro_ || coro_.done()) return false;
    coro_.resume();
    return !coro_.done();
  }
  T value() const { return coro_.promise().current_value; }

private:
  handle_type coro_;
};

Generator counter() {
  co_yield 0;
  co_yield 1;
  co_yield 2;
}

int main() {
  auto g = counter();
  while (g.next()) {
    std::cout << g.value() << '\n';
  }
  // 输出:0\n1\n2
}

关键点说明

这段代码展示了协程最核心的协作模式:

  • co_yield 触发 promise_type::yield_value(),保存值并挂起;
  • Generator::next() 调用 resume() 恢复执行,直到下次挂起或结束;
  • initial_suspend() 设为 suspend_always,让协程创建后不自动运行(惰性求值);
  • final_suspend() 也设为 suspend_always,防止协程结束后自动销毁 promise,方便外部安全读取结果。

常见误区提醒

初学容易踩坑的地方:

  • 忘记在 ~Generator 中调用 coro_.destroy() → 内存泄漏;
  • 把协程句柄(coroutine_handle)复制多次却不管理生命周期 → 悬空指针;
  • 误以为 co_await 等价于“异步等待” → 它只是语法糖,行为完全由 awaiter 决定(可以立刻返回,也可以调度到线程池);
  • 没处理 unhandled_exception() → 协程内抛异常会导致程序终止。

基本上就这些。协程本身不难理解,难点在于设计清晰的协程类型契约。建议从 generator 入手,再过渡到 async/await 风格的 task 类型(如 Task),逐步掌握 suspend/resume 的控制权。