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的这段内容翻译为中文:
协程状态是一个内部的、堆分配的对象(除非分配被优化掉),它包含:
- promise对象
- 参数(全部按值复制)
- 当前挂起点的某种表示,以便恢复时知道从何处继续,销毁时知道哪些局部变量在作用域内
- 生命周期跨越当前挂起点的局部变量和临时对象。
针对你的问题:
- 若直接使用
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
相关产品推荐
相关产品推荐

