-
Notifications
You must be signed in to change notification settings - Fork 15.3k
Description
A common problem with typical task implementations in many coroutine libraries is that the memory for the value to be returned is stored both in the coroutine promise and in frame local variables where it is constructed and returned. This introduces duplication, increases the frame size, and adds a call to the move constructor.
This problem is especially acute for large values or types with non-trivial move constructors, such as std strings.
And, ofc, it prevents returning non-movable types from coroutines, which is sad (but actually its possible with new attribute)
This problem can be solved by introducing a new attribute—its design is subject to debate, but the idea is simple: to give the compiler a hint where to construct the value in cases where, in a regular function, it would use the NRVO (named return value optimization)
example:
void bar(std::string&);
dd::task<std::string> foo() {
std::string s;
co_await std::suspend_always{};
bar(s);
co_await std::suspend_always{};
// in normal function this is NRVO, but in coroutines its promise.return_value(std::move(s))
co_return s;
}In my library i created additional tag this_coro::return_place, which is used like that:
void bar(std::string&);
dd::task<std::string> foo() {
// constructs value in-place in coro promise
std::string& s = co_await dd::this_coro::return_place;
co_await std::suspend_always{};
bar(s);
co_await std::suspend_always{};
co_return dd::rvo;
}without "NRVO":
https://godbolt.org/z/8YfTrc6K9
with "NRVO":