For c++20 coroutine, I am trying to understand whether a local variable in the coroutine needs to be allocated in the heap. I have the a runnable code in godbolt, which runs with expected output. Part of the code is listed below for our discussion.
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;
}
std::make_shared
?vec
in the heap? (no matter whether vec
is vector<int>
or shared_ptr<vector<int>>
)the coroutine state, which is an internal, heap-allocated (unless the allocation is optimized out), object that contains
- the promise object
- the parameters (all copied by value)
- some representation of the current suspension point, so that a resume knows where to continue, and a destroy knows what local variables were in scope
- local variables and temporaries whose lifetime spans the current suspension point.
The entire point of await-style coroutines (in pretty much every language that offers them) is that the code looks like synchronous code while having the ability to execute, to some degree, asynchronously. The await
-equivalent expressions don't fundamentally alter the nature of the code around them, and you can reason about the code as though everything happens in-order. For C++ coroutines, this includes automatic variables.
If you make a local vector<int>
, that variable works the same way regardless of whether its a coroutine. If you have this declaration: vector<int> vec = ...;
, you can use the name vec
to refer to that object at any pointer after its declaration, but before leaving the scope the declaration was placed within.
This is true whether there is a co_await
between those points or not. That's what "lifetime spans the current suspension point" means: accessing a variable whose lifetime began before some suspension point at a place after that suspension point.
Put simply, if you access a locally-declared object across suspension points, that object needs to be preserved somewhere, so that it still exists after the suspension and resumption of the coroutine. The wiki is explaining how that works.