NRVO 是否也适用于协程?

xml*_*lmx 4 c++ optimization standards c++20 c++-coroutine

在以下示例中,NRVO(命名返回值优化)适用于本文

std::string f1()
{
    std::string str;

    return str; // NVRO applies here!
}
Run Code Online (Sandbox Code Playgroud)

但是,请考虑:

task<std::string> f2()
{
    std::string str;

    co_return str; // Does NVRO also apply here?
}
Run Code Online (Sandbox Code Playgroud)

eer*_*ika 7

我知道 NRVO(命名返回值优化)从 C++17 开始是强制性的:

它不是。NRVO 仍然是一个优化。

未命名的返回值优化 (RVO) 是强制性的。

// Is NVRO also guaranteed here?
Run Code Online (Sandbox Code Playgroud)

不,因为 NRVO 永远无法保证。

  • @xmllmx 请注意该引用中缺少“Named”。另请参阅示例,其中没有一个返回左值。另请参阅“非强制复制副本”部分,其中特别提到了 NRVO。 (2认同)

jac*_*bsa 2

您链接的文章中定义的 NRVO(即,甚至不创建临时的)对于协程来说并不是什么,因为工作原理co_return取决于用户提供的协程承诺类型:语句中的表达式co_return被馈送到承诺的return_value方法,它可以决定如何处理它。

\n

然而,有一个相关的优化仍然可能有用。[class.copy.elision]/3表示以下内容:

\n
\n

隐式可移动实体是具有自动存储持续时间的变量,它可以是非易失性对象,也可以是对非易失性对象类型的右值引用。在以下复制初始化上下文中,在尝试复制操作之前首先考虑移动操作:

\n
    \n
  • 如果 return ([stmt.return]) 或 co_\xc2\xadreturn ([stmt.return.coroutine]) 语句中的表达式是一个(可能带括号的)id 表达式,它命名在主体或参数中声明的隐式可移动实体-最内层封闭函数或 lambda 表达式的声明子句,或者
  • \n
  • [...]
  • \n
\n

重载决议为副本或return_\xc2\xadvalue要调用的重载,就好像表达式或操作数是右值一样。如果第一次重载决策失败或未执行,则会再次执行重载决策,并将表达式或操作数视为左值。

\n
\n

这意味着,如果您从协程中按名称返回局部变量,它将被移动,而不是复制(只要 Promise 类型支持这一点)。例如,尽管不可能复制 a ,但 clang 仍接受以下内容std::unique_ptr<int>

\n
// Assume a coroutine task type called Task<T> whose associated promise has a\n// return_value(T) method. The co_return here will successfully call that\n// method.\nTask<std::unique_ptr<int>> MakeInt() {\n  auto result = std::make_unique<int>(17);\n  co_return result;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

因此,优化“即使std::move没有使用,也会将值作为右值引用提供给协程承诺”确实适用。但标准并没有说“甚至没有调用移动构造函数”,而且它也不能这样做,因为它取决于承诺如何处理它给出的表达式。

\n