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

C++20协程中局部变量的生命周期与堆分配相关疑问

C++20协程局部变量生命周期与堆分配问题解析

先看你提供的代码:

generator<int> my_coroutine()
{
  // Does vec needs to allocated in heap?
  // auto vec = std::make_shared<std::vector<int>>(std::initializer_list<int>{ 1, 2, 3, 4, 5 });
  auto vec = std::vector<int>{ 1, 2, 3, 4, 5 };
  for (auto&& i : vec)
  {
    co_yield i;
  }
}

int main()
{
  for (auto i : my_coroutine())
  {
    std::cout << i << " ";
  }
  std::cout << std::endl;
  return 0;
}

针对你的三个疑问逐一解答:

1. 局部变量vec的生命周期是否跨越当前挂起点?“跨越当前挂起点”具体指什么?

“跨越当前挂起点”的意思是:局部变量在协程挂起前已经构造完成,且协程从挂起状态恢复后,仍然需要访问或使用它——它的生命周期覆盖了“挂起→恢复”的完整过程,不会在挂起时被销毁。

回到你的代码:vec在co_yield执行前就完成初始化,每次co_yield触发协程挂起后,恢复时还会继续遍历vec的剩余元素。显然vec的生命周期完全覆盖了所有挂起点,因此它的生命周期是跨越挂起点的。

2. 是否需要使用std::make_shared在堆上分配vec?

不需要。

C++20协程机制会自动处理这类生命周期跨越挂起点的局部变量:它们会被存储到堆分配的协程状态对象中,由协程框架负责管理其生命周期(在协程最终结束时销毁)。你直接声明栈式的std::vector<int>即可,手动用智能指针堆分配反而会增加不必要的间接层和额外开销。

3. 关于cppreference中协程状态的描述,是否意味着协程状态会在堆上复制vec?

先将cppreference的这段内容翻译为中文:

协程状态是一个内部的、堆分配的对象(除非分配被优化掉),它包含:

  1. promise对象
  2. 参数(全部按值复制)
  3. 当前挂起点的某种表示,以便恢复时知道从何处继续,销毁时知道哪些局部变量在作用域内
  4. 生命周期跨越当前挂起点的局部变量和临时对象。

针对你的问题:

  • 若直接使用std::vector<int> vec:协程状态会将整个vec对象(包括它管理的内部数据缓冲区)包含在堆分配的协程状态中——这不是“复制”,而是vec本身会被构造在协程状态的内存区域(或被移动到此处,取决于编译器实现),存储位置从原本的栈帧转移到堆上的协程状态,生命周期与协程状态绑定。
  • 若使用std::shared_ptr<std::vector<int>> vec:协程状态会存储这个shared_ptr对象本身(而非vector的实际数据),vector的实际数据仍在堆上,由shared_ptr的引用计数管理。这种情况下,协程状态只是持有指向堆上vector的智能指针,而非vector对象本身。

简言之,两种写法都会让对应的变量(vector对象或shared_ptr对象)被放到堆上的协程状态中,但前者是vector整体在协程状态里,后者是智能指针在协程状态里、vector数据在独立堆内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 20:15:00