Consider the following code that defines the invoker
class - a minimal return type for a coroutine. We explicitly delete the copy and move constructors of the
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()
.
That would only be true if a coroutine function call were guaranteed to generate its return value by a call equivalent to building a bunch of objects in a separate stack, then calling get_return_object()
on one of them. That is, the question is whether the path from get_return_object()
to the function call itself only uses prvalues.
Let's look at 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.
Note that it says that it initializes the "prvalue result object". This is the same language used in the definition of the 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.
The only hesitation I would have in saying that the standard clearly requires guaranteed elision between get_return_object
and the caller of the coroutine is the last part about initial_suspend
. Because something happens between the initialization of the "prvalue result object" and returning control to the caller, it could be that there has to be an intermediary, which must be copied/moved from.
But the fact that it's using the exact same language as return
suggests that it ought to be providing the exact same behavior too.
When running on MSVC's coroutine implementation, your code (with only minor changes for differences in where certain types are defined) works fine. Coupled with the above evidence, I would say that this suggests that this is a compiler bug.