考虑以下定义 invoker
的代码class - 协程的最小返回类型。我们明确删除了 invoker
的复制和移动构造函数。类(class)。
#include <coroutine>
#include <cstdlib>
class invoker {
public:
class invoker_promise {
public:
invoker get_return_object() { return invoker{}; }
auto initial_suspend() { return std::suspend_never{}; }
auto final_suspend() { return std::suspend_never{}; }
void return_void() {}
void unhandled_exception() { std::abort(); }
};
using promise_type = invoker_promise;
invoker() {}
invoker(const invoker&) = delete;
invoker& operator=(const invoker&) = delete;
invoker(invoker&&) = delete;
invoker& operator=(invoker&&) = delete;
};
invoker f() {
co_return;
}
代码无法在 latest GCC 上编译(10.1),它应该完全支持 C++20 协程。
相反,我们收到一个错误,表明需要移动构造函数:
<source>: In function 'invoker f()':
<source>:23:1: error: use of deleted function 'invoker::invoker(invoker&&)'
23 | }
| ^
<source>:17:5: note: declared here
17 | invoker(invoker&&) = delete;
| ^~~~~~~
为什么会这样?
invoker
对象是通过调用 get_return_object()
来构造的的invoker_promise
,除非来自 f()
的调用者才能访问它.使用 C++17 保证复制省略,invoker
返回者 get_return_object()
是一个纯右值,因此在它从 f()
返回之前不应具体化.由于无法从协程内部访问返回的对象,因此我无法看到我们可能需要在返回对象之前具体化对象的任何情况。我错过了什么吗?
注意:我知道 this question , 但它:
最佳答案
With C++17 guaranteed copy elision, the
invoker
returned byget_return_object()
is a prvalue, and hence should not be materialized until after it is returned fromf()
.
只有当协程函数调用保证通过相当于在单独的堆栈中构建一堆对象的调用来生成其返回值,然后调用
get_return_object()
时,这才是正确的。其中之一。也就是说,问题是路径是否来自get_return_object()
函数调用本身只使用纯右值。我们来看what the standard says :
The expression
promise.get_return_object()
is used to initialize the glvalue result or prvalue result object of a call to a coroutine. The call toget_return_object
is sequenced before the call toinitial_suspend
and is invoked at most once.
请注意,它说它初始化了“纯右值结果对象”。这与 behavior of the
return
statement 的定义中使用的语言相同:the return statement initializes the glvalue result or prvalue result object of the (explicit or implicit) function call by copy-initialization from the operand.
我唯一的犹豫是说标准明确要求在
get_return_object
之间有保证的省略。协程的调用者是关于 initial_suspend
的最后一部分.因为在“纯右值结果对象”的初始化和将控制权返回给调用者之间发生了一些事情,所以可能必须有一个中间人,必须从中复制/移动。但事实上它使用与
return
完全相同的语言表明它也应该提供完全相同的行为。在 MSVC 的协程实现上运行时,您的代码(仅对某些类型定义的差异进行了微小的更改)works fine .结合上述证据,我会说这表明这是一个编译器错误。
关于c++ - 为什么协程的返回类型必须是可移动构造的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49725863/