#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\n";
}
std::string await_resume() noexcept{
std::cout<< "await_resume for FinalSuspend\n";
return "await_resume for FinalSuspend\n";
}
};
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\n";
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\n";
}
std::string await_resume(){
std::cout<<"await_resume\n";
return "abc";
}
};
struct Observe0{
Observe0(int v):id_(v){
std::cout<< id_ <<" constructor0\n";
}
~Observe0(){
std::cout<< id_ <<" destroy0\n";
}
Observe0(Observe0 const& v):id_(v.id_+1){
std::cout<< id_<<" copy constructor0\n";
}
Observe0(Observe0&& v):id_(v.id_+1){
std::cout<< id_<<" move constructor0\n";
}
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\n";
r.handler_.resume();
r.handler_.destroy();
std::cin.get();
}
The output https://godbolt.org/z/c8Wqejxox is:
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
。第三次打印发生在协程参数副本初始化时,由以下规则决定:
[dcl.fct.def.协程#13] https://eel.is/c++draft/dcl.fct.def.coroutine#13
当调用协程时,在初始化其参数([expr.call])后,将为每个协程参数创建一个副本。对于 cv T 类型的参数,副本是具有自动存储持续时间的 cv T 类型的变量,该变量是从引用该参数的 T 类型的 xvalue 直接初始化的。
这三个对象都有唯一的编号,方便观察关联对象的生命周期。根据第五次打印,析构函数被调用协程参数谁的名字是p
。然而,根据[expr.await#5.1] https://eel.is/c++draft/expr.await#5.1
否则,控制流返回到当前协程调用者或恢复者([dcl.fct.def.coroutine])不退出任何范围([stmt.jump])。
这意味着挂起协程并将控制权转移给调用者,协程的参数范围不被视为退出。因此,参数的生命周期不应结束。为什么参数的析构函数在第一次传递给协程的调用者之后才被调用?它应该被视为编译器中的错误吗?
参数的生命周期不属于函数的作用域;它是的一部分caller's scope https://timsong-cpp.github.io/cppwp/n4861/expr.call#7:
每个参数的初始化和销毁都发生在调用函数的上下文中。
这就是 [dcl.fct.def.coroutine#13] 存在的全部原因。为了让协程保留其参数,它必须拥有它们。这意味着must将它们从参数复制/移动到本地自动存储中。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)