在 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:
我注意到以下代码都被 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: