You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20协程实现生成器始终无法终止问题排查

问题分析与修复

你的Generator协程类无法正确判断迭代结束的核心问题出在promise_type的final_suspend实现,同时迭代逻辑也存在顺序错误,以下是具体说明和修复方案:

错误原因

  1. final_suspend返回值错误
    你当前的final_suspend()返回std::suspend_never,这意味着协程执行完最后一个co_yield后会立即销毁自身,不会进入挂起状态。而std::coroutine_handle::done()只有在协程处于完成且挂起的状态时才会返回true,所以你的handle永远无法触发done()为true的判断。

  2. 迭代逻辑顺序错误
    当前的循环中,你先检查gen(调用operator bool()),然后取值,再调用++gen恢复协程。但正确的顺序应该是:恢复协程→检查是否完成→取值,否则会导致最后一次取值后协程结束,但循环仍会继续执行。

修复后的代码

#include <iostream>
#include <coroutine>

template <typename T>
class Generator {
public:
    struct promise_type {
        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 {}; } // 关键修改:协程结束时挂起
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
        Generator get_return_object() { return Generator{ Handle::from_promise(*this) }; }
    };

    struct Handle : std::coroutine_handle<promise_type> {
        Handle(std::coroutine_handle<promise_type> h) : std::coroutine_handle<promise_type>(h) {}
        T& operator*() { return this->promise().value; } // 改用operator*更符合迭代器习惯
        void operator++() { this->resume(); }
        bool operator==(std::nullptr_t) const { return this->done(); }
        bool operator!=(std::nullptr_t) const { return !this->done(); }
    };

    Generator(Handle h) : handle(h) {}
    ~Generator() { if (handle) handle.destroy(); }

    // 实现迭代器接口,支持范围for循环
    Handle begin() { 
        handle.resume(); // 第一次恢复协程到第一个yield点
        return handle; 
    }
    std::nullptr_t end() { return nullptr; }

    // 兼容原有代码保留的运算符
    T& operator()() { return handle.promise().value; }
    void operator++() { ++handle; }
    explicit operator bool() { return !handle.done(); }

private:
    Handle handle{};
};

Generator<int> generateNumbers(int start, int end) {
   for (int i = start; i <= end; ++i) {
      co_yield i;
   }
}

int main()
{
    // 推荐使用范围for循环,更符合C++习惯
    auto gen = generateNumbers(1, 10);
    for (auto num : gen) {
        std::cout << num << std::endl;
    }

    // 兼容原有写法的修复版本
    /*
    auto gen = generateNumbers(1, 10);
    ++gen; // 先恢复到第一个yield点
    while (gen) {
        std::cout << gen() << std::endl;
        ++gen;
    }
    */
}

关键修改点说明

  • 将final_suspend()的返回值改为std::suspend_always,确保协程执行完毕后进入挂起状态,此时handle.done()会正确返回true。
  • 将initial_suspend()改为std::suspend_always,这是生成器的常规行为:创建后不立即执行,等待第一次resume才开始生成值。
  • 新增了完整的迭代器接口,支持范围for循环,代码更简洁易读。
  • 调整了原有循环的执行顺序:先调用++gen恢复协程,再判断是否完成,避免无效的取值操作。

内容的提问来源于stack exchange,提问作者Markus Fjellheim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 19:17:09