UB是否可以恢复其生存期已结束的对象的成员函数协程?


9

这个问题源于以下评论:C ++ 20协程的Lambda生命周期说明

关于这个例子:

auto foo() -> folly::coro::Task<int> {
    auto task = []() -> folly::coro::Task<int> {
        co_return 1;
    }();
    return task;
}

所以问题是执行返回的协程是否foo会导致UB。

“调用”成员函数(在对象的生存期结束之后)是UB:http : //eel.is/c++draft/basic.life#6.2

...可以使用任何表示对象将要或曾经位于的存储位置的地址的指针,但只能以有限的方式使用。[...]该程序在以下情况下具有未定义的行为:

[...]

-指针用于访问对象的非静态数据成员或调用该对象的非静态成员函数,或者

但是,在此示例中:

  • ()Lambda的生存期仍然有效时,将调用Lambda 的运算符
  • 然后将其挂起,
  • 然后lambda被破坏,
  • 然后成员函数(operator ())随后恢复。

是否将此恢复视为未定义的行为?


2
也许以下答案是相关的stackoverflow.com/a/60495359/12345656似乎很不一样,但这也与成员函数有关,在该成员函数执行期间this指针无效。还考虑评论中的讨论。
n314159

Answers:


2

[dcl.fct.def。协程] p3

协程的promise类型std::coroutine_traits<R, P1, ..., Pn>::promise_type,其中R是函数的返回类型,并且P1 ... Pn是函数参数的类型序列,如果协程是非静态的, 则以 隐式对象参数(12.4.1)的类型开头成员函数。

在您的示例中,隐式对象参数是const引用,因此,在销毁闭包对象后恢复执行时,该引用将处于悬挂状态。

但是,关于在执行成员函数期间销毁对象的注意事项,这本身的确是可以的,并且除标准本身外,[basic]中没有其他暗示:

在对象的生存期开始之前但已分配了该对象将要占用的存储空间之后,或者在对象的生存期结束之后以及该对象占用的存储空间被重用或释放之前,任何表示该地址的指针可以使用对象将要位于的存储位置,但只能以有限的方式使用。[...]

void B::mutate() {
  new (this) D2;    // reuses storage --- ends the lifetime of *this
  f();              // undefined behavior
  ... = this;       // OK, this points to valid memory
}

(注意:上面的UB是因为this没有清洗隐式对象,而是仍然引用隐式对象参数。)

因此,您的示例似乎定义明确,但前提条件是恢复执行不属于与原始调用相同的规则。请注意,对闭合对象的引用可能是悬空的,但是在挂起和恢复之间没有任何访问方式。


您最后是否表示“恢复和完成”?
戴维斯·鲱鱼

@DavisHerring不,我的意思是明确地在那个“外部”时间范围内,不清楚该参考是否可以分配给需要真实对象的新参考等。不要被UB,参考不以隐藏方式访问的事实是这一重要
科伦坡

但是仅保留悬挂的参考文献直到恢复是不够的。您必须永久保留它(例如,在lambda体内),直到它的余生,直到完成。因此,也许应该是“悬而未决”。
戴维斯鲱鱼

@DavisHerring我特别提到了该间隔,因为在我们的示例中我们知道另一个是安全的。
哥伦布

当然; 我只是觉得措辞令人困惑。也许没有人这样做。
戴维斯鲱鱼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.