【发布时间】:2021-06-28 01:03:29
【问题描述】:
#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();
}
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 处的协程参数初始化时。第三次打印发生在初始化协程参数拷贝时,由以下规则决定:
[dcl.fct.def.coroutine#13]
当调用协程时,在初始化其参数([expr.call])后,会为每个协程参数创建一个副本。对于 cv T 类型的参数,副本是具有自动存储持续时间的 cv T 类型的变量,该变量是从引用该参数的 T 类型的 xvalue 直接初始化的。
这三个对象都有自己的唯一编号,便于观察关联对象的生命周期。根据第五次打印,析构函数是为名为@987654329@ 的协程参数 调用的。不过根据[expr.await#5.1]
否则,控制流返回到当前协程调用者或恢复者 ([dcl.fct.def.coroutine]) 不退出任何范围 ([stmt.jump])。
表示挂起协程并将控制权转移给调用者,协程的参数范围不视为退出。因此,参数的生命周期不应结束。为什么参数的析构函数在第一次转移到协程的调用者之后被调用?它应该被认为是编译器中的错误吗?
【问题讨论】:
标签: c++ language-lawyer c++20 coroutine