【问题标题】:What's the lifetime of the parameter of a coroutine协程参数的生命周期是多少
【发布时间】: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 直接初始化的。

这三个对象都有自己的唯一编号,便于观察关联对象的生命周期。根据第五次打印,析构函数是为名为@9​​87654329@ 的协程参数 调用的。不过根据[expr.await#5.1]

否则,控制流返回到当前协程调用者或恢复者 ([dcl.fct.def.coroutine]) 不退出任何范围 ([stmt.jump])。

表示挂起协程并将控制权转移给调用者,协程的参数范围不视为退出。因此,参数的生命周期不应结束。为什么参数的析构函数在第一次转移到协程的调用者之后被调用?它应该被认为是编译器中的错误吗?

【问题讨论】:

    标签: c++ language-lawyer c++20 coroutine


    【解决方案1】:

    参数的生命周期不是函数作用域的一部分;它是caller's scope 的一部分:

    每个参数的初始化和销毁​​发生在调用函数的上下文中。

    这就是 [dcl.fct.def.coroutine#13] 存在的全部原因。为了让协程保留其参数,它必须拥有它们。这意味着它必须将它们从参数复制/移动到本地自动存储中。

    【讨论】:

    • 每个参数的初始化和销毁​​发生在调用函数的上下文中。这仅仅意味着参数的初始化或销毁是调用者的责任。但是,对象销毁的前提是对象生命周期结束。 参数的生命周期是在定义它的函数返回时结束,还是在封闭的完整表达式结束时结束,这是实现定义的。 暗示了这一点。暂停协程似乎并不意味着任何一种情况。
    • @xmh0511:就“调用函数”而言,暂停协程算作从函数调用返回。我的观点是参数的生命周期不受被调用的函数的控制。它们不是其“范围”的一部分,因此不会被协程的暂停保留。
    • 参数的作用域是函数参数作用域,它包含了由函数的复合语句引入的块作用域。因此,参数在该范围内。 析构函数被隐式调用:对于具有自动存储持续时间([basic.stc.auto])的构造对象,当创建对象的块退出时;在每种情况下,调用的上下文都是对象构造的上下文。 所以,正如你所说,它在调用者的上下文中。如果实现选择...
    • ...“参数的生命周期在定义它的函数返回时结束”,挂起确实根据控制流返回返回给调用者> 到当前协程调用者或恢复者。但是,如果它选择后者,我不知道如何解释它(我的意思是,是否暂停协程并返回计数是否进入封闭的完整表达式的末尾?)。
    • @LanguageLawyer 在每种情况下,调用的上下文都是对象构造的上下文 class.dtor#15 提到了这一点。简单来说,调用者负责销毁对象,因为它构造了对象。
    猜你喜欢
    • 2023-02-24
    • 2019-10-09
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 2021-12-24
    • 2016-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多