[英]destruction order of promise and return object for C++ coroutine
#include <iostream>
#include <coroutine>
class eager {
public:
struct promise_type {
promise_type() { std::cout << "promise_type ctor" << std::endl; }
~promise_type() { std::cout << "~promise_type dtor" << std::endl; }
struct return_object {
return_object() { std::cout << "return_object ctor" << std::endl; }
~return_object() { std::cout << "~return_object dtor" << std::endl; }
operator eager() { return {}; }
};
auto get_return_object() noexcept { return return_object{}; }
constexpr auto initial_suspend() const noexcept { return std::suspend_never{}; }
constexpr auto final_suspend() const noexcept { return std::suspend_never{}; }
constexpr auto return_void() const noexcept {}
auto unhandled_exception() -> void { throw; }
};
};
auto coroutine() -> eager {
co_return;
}
auto main() -> int
{
coroutine();
return 0;
}
您可以在此处查看 MSVC、clang 和 GCC 的结果: https://godbolt.org/z/Yan9s9TPE
根据很多关于协程的文章, coroutine()
将被转换成...
auto coroutine() -> eager {
eager::promise_type promise;
auto res = promise.get_return_object();
// initial suspend
promise.return_void();
// final suspend
return res;
}
一眼看去,由于promise object是先构建的,我还以为是最后一个object被析构。
但是,MSVC 和 GCC 显示相反的顺序:
// from MSVC/GCC
promise_type ctor
return_object ctor
~promise_type dtor
~return_object dtor
另一方面,clang 显示了我的预期:
// from clang
promise_type ctor
return_object ctor
~return_object dtor
~promise_type dtor
哪一个是对的? 或者,promise object 的销毁顺序和返回 object 是否只是标准未指定?
根据很多关于协程的文章
那么“很多关于协程的文章[原文如此]”是不正确的。
结果 object 不在协程栈上。 它不能在协程堆栈上,因为它是协程初始调用的结果 object 。
C++ 标准对get_result_object
的描述是这样的:
表达式
promise.get_return_object()
用于初始化协程调用的右值结果或右值结果 object。 对 get_return_object 的调用在对initial_suspend
的调用之前排序,并且最多调用一次。
它发生在initial_suspend
之前并且被调用一次。 这就是它要说的全部。 因此,函数的结果对象的其他一切都正常工作; 在这种情况下,它只是在 function 正确启动之前初始化,而不是在 function 即将返回时初始化。
按照 C++ 的一般规则,函数的结果 object 在调用者的堆栈上,而不是被调用的 function 的堆栈上。 因此,当评估promise.get_result_object()
时,它正在初始化调用者提供的存储。
main
丢弃表达式coroutine()
的结果。 这意味着它将根据 prvalue 结果 object 显示一个临时文件,并且此临时文件的类型将为eager
。 临时文件将被销毁,但只有在控制权返回main
之后。
这是棘手的部分: get_result_object()
的返回纯右值并不eager
。 它是eager::promise::result_object
。 初始化返回值需要执行从result_object
到eager
的隐式转换。 这需要显示一个result_object
类型的临时对象来执行该转换。
临时对象作为评估完整表达式 ([intro.execution]) 的最后一步被销毁,该表达式(在词法上)包含它们的创建点。
但是……这里的“完整表达”是什么?
人们会假设它是promise.get_result_object()
表达式。 但这是 C++ 规则的“完整表达式”吗? 这些规则相当深奥和技术性。 有人可能会争辩说promise.get_result_object()
被用来初始化一个 object,因此它实际上是一个“init-declarator”。 但是“init-declarator”是一段语法, promise.get_result_object()
并没有被文本声明为“init-declarator”。
可以说唯一肯定是完整表达式的表达式是coroutine()
。 因此,可以提出这样的论点,即任何用于初始化返回值 object 的临时对象都应该持续存在,直到控制权返回给调用者。
我认为标准措辞未明确规定,因此在提供澄清之前,这两个版本同样合法。 Clang 的版本更有意义(但不是出于您声称的原因),但其他版本至少是有争议的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.