协程参数的生存期是多少?
#include <iostream>
#include <experimental/coroutine>
#include <string>
#include <thread>
struct InitialSuspend{
bool await_ready(){
return false;
}
bool await_suspend(std::experimental::coroutine_handle<> h){
return false;
}
void await_resume(){
}
};
struct FinalSuspend{
bool await_ready() noexcept{
return false;
}
void await_suspend(std::experimental::coroutine_handle<> h) noexcept{
std::cout<<"FinalSuspend await_suspend
";
}
std::string await_resume() noexcept{
std::cout<< "await_resume for FinalSuspend
";
return "await_resume for FinalSuspend
";
}
};
struct Task{
struct promise_type;
using coroutine_type = std::experimental::coroutine_handle<promise_type>;
struct promise_type{
auto initial_suspend(){
return InitialSuspend{};
}
void unhandled_exception(){
std::cout<<"unhandled_exception
";
std::terminate();
}
auto final_suspend() noexcept{
return FinalSuspend{};
}
// void return_value(std::string const& v){
// value_ = v;
// }
void return_void(){
}
auto get_return_object(){
return Task{coroutine_type::from_promise(*this)};
}
std::string value_;
};
coroutine_type handler_;
};
struct AwaitAble{
bool await_ready(){
return false;
}
void await_suspend(std::experimental::coroutine_handle<> h){
std::cout<<"await_suspend
";
}
std::string await_resume(){
std::cout<<"await_resume
";
return "abc";
}
};
struct Observe0{
Observe0(int v):id_(v){
std::cout<< id_ <<" constructor0
";
}
~Observe0(){
std::cout<< id_ <<" destroy0
";
}
Observe0(Observe0 const& v):id_(v.id_+1){
std::cout<< id_<<" copy constructor0
";
}
Observe0(Observe0&& v):id_(v.id_+1){
std::cout<< id_<<" move constructor0
";
}
int id_;
};
Task MyCoroutine(Observe0 p){
auto r1 = co_await AwaitAble{};
}
int main(){
Observe0 aa{1}; //#1
auto r = MyCoroutine(aa); //#2
std::cout<<"caller
";
r.handler_.resume();
r.handler_.destroy();
std::cin.get();
}
output为:
1 constructor0
2 copy constructor0
3 move constructor0
await_suspend
2 destroy0
caller
await_resume
FinalSuspend await_suspend
3 destroy0
1 destroy0
我们可以使用上面的代码观察对象的创建或销毁。第一次打印发生在#1
,它构造对象a
。第二次打印发生在#2
处的协程参数初始化时。第三次打印发生在协程参数COPY的初始化,它遵循以下规则:
[dcl.fct.def.coroutine#13]
当调用协程时,在初始化其参数之后([expr.call]),将为每个协程参数创建一个副本。对于cv T类型的参数,副本是cv T类型的变量,具有自动存储持续时间,它是从引用该参数的类型T的x值直接初始化的。这三个对象都有其唯一的编号,这便于观察关联对象的生命周期。根据第五个打印,为协程参数调用析构函数,其名称为
p
。然而,根据[expr.await#5.1]
否则,控制流会返回到当前的协程调用方或恢复方([dcl.fct.Def.coroutine]),而不退出任何作用域([stmt.jump])。
意味着挂起协程并将控制权移交给调用方,协程的参数范围不被视为退出。因此,参数的生存期不应结束。为什么在第一次传递给协程的调用方之后调用参数的析构函数?是否应将其视为编译器中的错误?
解决方案
参数的生存期不是函数作用域的一部分;它是caller's scope:
的一部分每个参数的初始化和销毁都在调用函数的上下文中进行。
这就是[dcl.fct.de.coroutine#13]存在的全部原因。为了让协程程序保持其参数,它必须拥有这些参数。这意味着必须将它们从参数复制/移动到本地自动存储中。
相关文章