协程co_await操作中,操作对象类型是否必须具备复制或移动构造能力?MSVC与GCC编译差异分析
co_await Scenario? Let's walk through this problem step by step, tying each compiler's behavior back to the C++20 coroutine standard.
First, recap the scenario: we have a task coroutine return type with deleted copy and move constructors. We create a suspended coroutine f, then define another coroutine g that runs co_await f. MSVC compiles this code without issues, but GCC 11.2 throws an error about a missing move constructor, and GCC trunk complains about a missing copy constructor.
What the C++ Standard Says About co_await
To resolve this, we need to reference the rules for co_await expressions (defined in the [expr.await] section of the C++20 standard):
- Evaluating the operand: When we write
co_await f,fis a left-value reference to ourtaskobject. The standard does not require copying or moving the operand here—we can work directly with the reference. - Obtaining the awaiter: Our
taskclass already implements the three required awaiter methods (await_ready,await_suspend,await_resume), so thetaskobject itself acts as the awaiter. There's no need to create a newtaskinstance; we can use the existingfvia its reference. - Lifetime safety: Since
fis declared inmainand we resumegwithinmain,f's lifetime covers the entireco_awaitoperation. The standard does not force implementations to copy the awaitable object when a valid, long-lived reference is available.
Analyzing Compiler Behavior
- MSVC: It correctly uses the reference to
fthroughout theco_awaitprocess, never attempting to copy or move thetaskinstance. This matches the standard's requirements perfectly—there's no rule mandating duplication of the awaitable object when a reference works. - GCC: Both versions of GCC incorrectly try to copy or move
fduringco_awaitprocessing. This isn't required by the standard, and the inconsistency between versions (requesting move vs copy constructors) points to an implementation bug in how GCC handles reference operands forco_await.
Conclusion
MSVC's behavior aligns with the C++ standard, while GCC's behavior here is incorrect due to an unnecessary attempt to copy or move the awaitable object.
内容的提问来源于stack exchange,提问作者Fedor

