传递给协程的临时对象何时被销毁?

19

我对传递给协程任务的临时对象的生命周期感到困惑。考虑这个例子:

cppcoro::task<> UseObject(Object const& object);

cppcoro::task<> CallUseObject() {
    co_await UseObject(Object()); // is this valid?
}
如果这些函数返回空,那么传递给UseObject的Object()将在分号处被销毁(即在UseObject完成后)。但是,我不确定同样的规则是否适用于协程。通过引用将临时对象传递给协程是否安全?如果不是在分号处销毁,那么临时对象何时被销毁?
此外,作为一个健全性检查,总是写下以下代码是否安全:
cppcoro::task<> CallUseObject() {
    Object stayingalive;
    co_await UseObject(stayingalive);
}

因为co_await完成后staying_alive会被销毁?


我很想说Object()在语句结束时已经死了,但我可能是错的。协程似乎是“按值传递”的一个很好的选择。 - Guillaume Racicot
如果函数调用协程并通过引用接收参数,则调用方函数负责传递的对象的生命周期,直到协程完成。这实际上与同步调用的规则相同,但现在您还应该处理函数退出但未完成以及所有临时对象被销毁的情况。co_await运算符无法帮助,因为此运算符仅在函数退出后才起作用。 - jenkas
我最近使用vs2019的经验表明,在第一个示例(Object())中,析构函数直到co_await结束才被调用。 - lostyzd
1个回答

12
这实际上是当前草案中一个未解决问题的主题。引用该问题:
意图是创建参数的拷贝/移动(如果需要),保留完全相同的类型(包括引用、r-引用等)。11.4.4 [dcl.fct.def.coroutine] / 11中的措辞似乎没有清晰地表达出来。
基于此,协程框架将捕获对临时变量的引用。
由于co_await是一个表达式,在它所在的完整表达式结束时,临时对象应该被销毁。您上面的代码是否安全取决于涉及的两个协程的具体实现是否安全,以使用对临时对象的引用进行co_await。特别要注意的是,co_await的操作取决于它所应用的表达式的类型以及它所出现的协程的承诺类型。此外,UseObject(我们不知道其定义)原则上可能会对其给定的引用执行各种奇怪的操作...

2
我认为这部分回答了我的问题。你是说协程帧应该持有一个引用。问题的另一半是当引用失效时。对象在co_await完成之前被销毁吗?还是在分号处。如果我理解有误,请纠正我。 - sudo rm -rf slash
1
我认为确实应该发生在分号处。co_await是一个表达式。当prvalue Object()绑定到引用时,产生的临时对象将在完整表达式结束时销毁。 - Michael Kenzel
好的。根据您的评论,这段代码是安全的,因为协程存储了对临时变量的引用,该变量在协程销毁后被销毁。如果您将您的回答和评论合并,我会接受这个答案。 - sudo rm -rf slash
@sudorm-rfslash 我认为一般来说不能说这是安全的。这是否安全取决于 co_await 将要做什么。co_await 的操作取决于参数的类型以及它所在的协程的 promise 类型。根据你提供的信息,我们只能说协程调用将会捕获对临时对象的引用。这是否安全取决于这两个协程的具体实现... - Michael Kenzel
感谢您的更新。我希望委员会在标准化协程之前能够简化这些简单问题的答案。 - sudo rm -rf slash
@sudorm-rfslash,这个功能已经被投票列入标准了,我认为不会有太多变化。不过,假设对UseObject调用上的 co_await确实等待UseObject引用完成操作,那么这将是安全的(如果我们还假设UseObject不会做任何傻事,比如使用该引用初始化一个全局变量以便以后使用)。我只是试图保持我的回答在一般情况下都是正确的。特别是考虑到将来可能有人会发现这个问题,并且可能会误以为这在一般情况下是安全的… - Michael Kenzel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接