c++ - 为什么协程的返回类型必须是可移动构造的?

标签 c++ coroutine c++20

考虑以下定义 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 , 但它:
  • 两年前问的,
  • 是关于协程的TS版本,
  • 是关于VC++的实现,
  • 无人接听,和
  • 有评论主要谈论保证复制省略。
  • 最佳答案

    With C++17 guaranteed copy elision, the invoker returned by get_return_object() is a prvalue, and hence should not be materialized until after it is returned from f().



    只有当协程函数调用保证通过相当于在单独的堆栈中构建一堆对象的调用来生成其返回值,然后调用 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 to get_­return_­object is sequenced before the call to initial_­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/

    相关文章:

    c++ - signed 和 normal short 有什么区别

    c++ - std::weak_ptr<T>::lock 是线程安全的吗?

    c++ - 具有依赖参数类型的概念约束成员函数

    c++ - 无法获取字符串的长度

    c++ - 为什么在死锁的情况下 lock() 不抛出异常

    c++ - 使用 pugixml 或实际的 HTML 解析器解析 HTML

    android - 异步结果的类型是什么?

    python - 大量递归请求的 Tornado 超时

    scheme - 求人为的示例代码: continuations!

    c++ - C++ 编译时调试