讲师中心 微信公众号

工具推荐

视频效率加速
立刻MV
立刻MV

低成本歌曲 MV、视频生成

updream

一键生成高质量推广成片

Seko
Seko

AI短剧一键生成成片

Laper
Laper

剧本策划到成片协作

C++怎么使用generator_C++23协程生成器教程【迭代】

星辰君_9469

星辰君_9469

发布时间:2026-02-21 09:53:13

|

702人浏览过

|

来源于php中文网

原创

C++23中需手动实现generator:基于std::coroutine_handle与自定义promise_type,显式调用next(),不可直接用于range-for;co_yield挂起点由yield_value返回awaitable的await_suspend决定;须谨慎处理生命周期与跨编译器ABI差异。

c++怎么使用generator_c++23协程生成器教程【迭代】

怎么在 C++23 里写一个最简 generator

标准库没提供 generator 类型,得自己定义——但别急着抄 Boost 或第三方实现。C++23 协程规范里明确要求编译器支持 co_yield 和自定义协程句柄,std::generator 是提案(P2165),尚未进标准库(截至 GCC 14 / Clang 18 / MSVC 19.38)。所以你现在能用的,是基于 std::coroutine_handle + 自定义 promise_type 的轻量 generator 模板。

最简可用版本长这样:

template<typename T>
class generator {
  struct promise_type;
  using handle_type = std::coroutine_handle<promise_type>

public:
  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() {}
    auto yield_value(T v) {
      current_value = std::move(v);
      return std::suspend_always{};
    }
    void unhandled_exception() { std::terminate(); }
  };

private:
  handle_type h_;

public:
  explicit generator(handle_type h) : h_(h) {}
  ~generator() { if (h_) h_.destroy(); }
  generator(const generator&) = delete;
  generator& operator=(const generator&) = delete;
  generator(generator&& o) noexcept : h_(o.h_) { o.h_ = {}; }
  generator& operator=(generator&& o) noexcept {
    if (h_) h_.destroy();
    h_ = o.h_;
    o.h_ = {};
    return *this;
  }

  T next() {
    if (!h_ || h_.done()) throw std::runtime_error("generator exhausted");
    h_.resume();
    if (h_.done()) throw std::runtime_error("generator yielded after final suspend");
    return h_.promise().current_value;
  }
};

关键点不是“怎么写全”,而是:你必须显式调用 next(),不能直接用 range-for(除非补 begin()/end() 和迭代器);yield_value 返回 std::suspend_always 是为了确保每次 co_yield 后停住;initial_suspend 也设为 std::suspend_always,否则构造时就执行到第一个 co_yield 前,可能出未定义行为。

为什么 range-for 不能直接用 generator

因为标准 range-for 要求类型有 begin() 和 end(),且返回的迭代器要满足 input_iterator 概念。而裸 generator 不是 range,它只是个可恢复的计算过程容器。

立即学习“C++免费学习笔记(深入)”;

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载
  • 强行加 begin()/end() 需额外封装一层(比如 generator_range),还要处理移动语义和多次遍历问题
  • 每次 range-for 都会尝试构造新迭代器,但 generator 是一次性消耗品(resume() 后无法 rewind)
  • 如果 generator 内部有局部变量或捕获状态,复制对象会导致悬垂引用或重复析构
  • Clang/GCC 当前对协程的 range 支持不一致:Clang 17+ 允许 for (auto x : f()) 如果 f() 返回带合适 begin/end 的类型;GCC 14 还会报 no viable conversion

所以别硬套语法糖。老老实实用 while + try/catch 更可控:

auto g = make_fibonacci();
while (true) {
  try {
    std::cout << g.next() << "\n";
  } catch (const std::runtime_error&) {
    break;
  }
}

co_yield 后挂起的位置到底在哪

很多人以为 co_yield expr 等价于 “计算 expr → 存进 promise → 挂起”,其实挂起发生在 yield_value 返回的 awaitable 的 await_suspend 执行之后。也就是说,真正暂停点取决于你 yield_value 返回的那个对象的 await_suspend 实现。

  • 返回 std::suspend_always{}:立刻挂起,控制权交还给调用方
  • 返回 std::suspend_never{}:不挂起,协程继续往下跑(危险!容易栈溢出或逻辑错乱)
  • 如果你返回自定义 awaitable,并在 await_suspend 里调度到线程池,那挂起时机就由调度器决定——这已超出 generator 基本用途
  • 调试时注意:GDB/LLDB 对协程帧支持有限,co_yield 行可能显示为 “not executable”,实际断点得打在 yield_value 或 await_suspend 里

MSVC / GCC / Clang 在协程 ABI 上的坑

三大编译器对协程的内存布局、promise 构造时机、异常传播路径实现不一致,尤其影响 generator 的跨平台稳定性。

  • MSVC 默认把 promise 分配在协程帧内(stack-allocated),但若 promise 有非平凡析构函数,可能触发未定义行为;建议加 #pragma clang system_header 或用 /Zc:coroutines- 关闭优化干扰
  • GCC 13–14 中,若 generator 函数参数含右值引用,co_yield 可能绑定到已销毁的临时对象(生命周期分析 bug),规避方式:全部传值或 const lvalue 引用
  • Clang 17 开始支持 -fcoroutines-ts,但若混用 libstdc++(GCC 标准库)会链接失败,必须用 libc++ 编译整个项目
  • 所有编译器都不保证协程帧分配在堆上——所以不要假设 generator 对象比其内部 handle 活得久;析构顺序错位极易 crash

generator 不是语法糖,它是手动管理协程生命周期的薄封装。最易被忽略的是:你永远得自己确保 promise 的 lifetime 覆盖整个 resume 过程,而不是依赖 RAII 自动管理。

热门AI工具

更多
二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

函子Zion

一款支持AI搭建、可视化编辑和多端发布的全栈无代码开发平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

354

2023.09.25

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

2458

2023.09.20

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

7007

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2888

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

7007

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2888

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4858

2023.08.10

promise的用法
promise的用法

“promise” 是一种用于处理异步操作的编程概念,它可以用来表示一个异步操作的最终结果。Promise 对象有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。Promise的用法主要包括构造函数、实例方法(then、catch、finally)和状态转换。

4900

2023.10.12

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

20

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn