在 C++20 中删除协程的 copy/move 函数并实例化它是否合法?

Is it legal to delete copy/move functions of a Coroutine in C++20 and instantiate it?

我注意到以下代码都被 MSVC and GCC 接受(下面是简化的 TLDR 版本):

template<typename T>
struct Generator {
    struct promise_type {
        std::suspend_always initial_suspend() {
            return {};
        }
        std::suspend_always final_suspend() noexcept {
            return {};
        }
        auto get_return_object() {
            return Generator{ std::coroutine_handle<promise_type>::from_promise(*this) };
        }

        std::suspend_always yield_value(T value) {
            current_value = value;
            return {};
        }
        void return_void() {}
        void unhandled_exception() {
            std::exit(1);
        }

        T current_value;
    };
    
    Generator(std::coroutine_handle<promise_type> h): coro(h) {}                

    std::coroutine_handle<promise_type> coro;
    
    ~Generator() {
        if (coro) coro.destroy();
    }
    Generator(const Generator&)              = delete;
    Generator& operator = (const Generator&) = delete;
    Generator(Generator&&)                   = delete;
    Generator& operator = (Generator&&)      = delete;

    T operator()() {
        coro.resume();
        return coro.promise().current_value;
    }

};

Generator<int> getNext() {
    int value = 0;
    while (true) {
        co_yield value;
        value++;
    }
}

int main() {
    Generator<int> gen = getNext();
    for (int i = 0; i <= 2; ++i) {
        int val = gen();
        std::cout << "val: " << val << std::endl;
    }
    
}

TLDR 版本:

template<typename T>
struct Generator {
    struct promise_type {
        //...
    };
    
    //...
    
    Generator(const Generator&)              = delete;
    Generator& operator = (const Generator&) = delete;
    Generator(Generator&&)                   = delete;
    Generator& operator = (Generator&&)      = delete;

    //...

};

Generator<int> getNext() {
    //...
}

int main() {
    Generator<int> gen = getNext();
    //...
}

不过我想知道 C++20 标准是否允许这样做。通常,如果删除复制和移动功能,则无法通过功能检索类型:

struct MyType
{
    MyType() {}

    MyType(const MyType&)              = delete;
    MyType& operator = (const MyType&) = delete;
    MyType(MyType&&)                   = delete;
    MyType& operator = (MyType&&)      = delete;
};

MyType generateType()
{
    MyType val;
    return val; // <--- Compile Error
}

这一行乍一看似乎也使用了删除的函数:Generator<int> gen = getNext();

协程允许这样做吗?如果是,标准的哪一部分允许这样做?

我相信 Generator 对象是通过调用 get_return_object() 来检索的,returns 是一个纯右值。从 C++17 开始,这需要强制复制省略。见 cppreference.com:

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type: