NRVO 是否也适用于协程?
Does NRVO also apply to coroutines?
在以下示例中,NRVO(命名返回值优化(按照本文应用:
std::string f1()
{
std::string str;
return str; // NVRO applies here!
}
但是,请考虑:
task<std::string> f2()
{
std::string str;
co_return str; // Does NVRO also apply here?
}
NRVO(命名返回值优化(自 C++17 年以来是强制性的:
不是。NRVO仍然是一种优化。
非命名的返回值优化 (RVO( 是强制性的。
// Is NVRO also guaranteed here?
不,因为NRVO永远无法保证。
为了完整起见,C++17 中的保证省略仅适用于直接从函数返回 prvalue。仅当编译器愿意时,返回命名变量才会受到省略的影响。
至于你问题的实质,co_return
价值永远不会受到复制省略,保证或其他方式的影响。返回值的 Elision 键关闭return
关键字,并且协程不允许使用return
.他们使用co_return
,标准中的省略逻辑不会关闭。所以省略不适用。
这样做的原因是协程的工作方式。协程是包含 promise 对象的函数。此 promise 对象是将协程的co_return
值(和其他状态(引导到协程函数返回的"未来"对象的方式。
Elision 在普通函数中工作,因为调用约定要求调用方将返回值的存储传递给函数。因此,函数的实现可以选择仅在该存储中构建对象,而不是构建一个单独的堆栈对象并在return
时复制到其中。
在协程中,返回值位于承诺中,因此这不会真正发生。
您链接的文章定义的 NRVO(即甚至不创建临时(对于协程来说不是一件事情,因为co_return
的工作方式取决于用户提供的协程 promise 类型:co_return
语句中的表达式被馈送到 promise 的return_value
方法,该方法可以决定如何处理它。
但是,有一个相关的优化仍然可能有用。[class.copy.elision]/3 说:
隐式可移动实体是自动存储持续时间的变量,它是非易失性对象或对非易失性对象类型的右值引用。在以下复制初始化上下文中,在尝试复制操作之前,首先考虑移动操作:
- 如果返回 ([stmt.return]( 或 co_return ([stmt.return.coroutine]( 语句中的表达式是一个(可能用括号括起来的(id 表达式,它命名在最里面的封闭函数或 lambda 表达式的主体或参数声明子句中声明的隐式可移动实体,或者
- [...]
首先执行重载解析以选择要调用的副本或
return_value
重载,就好像表达式或操作数是右值一样。如果第一个重载解析失败或未执行,则再次执行重载解析,将表达式或操作数视为左值。
这意味着,如果您从协程按名称返回局部变量,它将被移动,而不是复制(只要 promise 类型支持这一点(。例如,尽管无法复制std::unique_ptr<int>
,但 clang 接受以下内容:
// Assume a coroutine task type called Task<T> whose associated promise has a
// return_value(T) method. The co_return here will successfully call that
// method.
Task<std::unique_ptr<int>> MakeInt() {
auto result = std::make_unique<int>(17);
co_return result;
}
因此,优化"即使未使用std::move
值也作为右值引用提供给协程承诺"确实适用。但是标准并没有说"甚至没有调用移动构造函数",它不能,因为它取决于如何处理它给出的表达式的承诺。
只是因为答案看起来太长了。该标准规定,语句co_return <expr>;
等效于:
P.return_value(<expr>);
其中P
是协程的承诺对象。由此我想你可以回答这个问题和许多其他问题。
如果您正在寻找协程文档,请查看此处:
- dcl.fct.def.coroutine
- stmt.return.coroutine
- expr.await
- expr.yield
- 支持.协程
- 确定夏令时是否适用于特定日期
- 是否有一种 STL 算法可以最后找到,但它也适用于指针?
- C++17 和更新的 std::分配器是否适用于动态数量的自定义堆?
- NRVO 是否也适用于协程?
- 约束包容是否仅适用于概念?
- std::mutex 的发布-获取可见性保证是否仅适用于关键部分?
- 使用迭代器成员函数是否仅适用于某些向量类型"empty()"?
- 别名漏洞是否适用于签名字符?
- 指针算法是否适用于迭代器?
- 保证复制 elis 是否适用于函数参数?
- 尾部调用优化是否适用于此功能?
- 值初始化是否适用于原子对象?
- 是否有适用于迭代器的数字解析函数
- 严格别名规则是否适用于跨函数调用
- Scott Meyers关于首选非成员非友元方法的建议是否适用于对象构造?
- 对函数参数的要求是否也适用于初始值设定项列表?
- 在这种情况下,(N)RVO是否适用于我的功能
- 是否有适用于张量流对象检测 API 的C++包装器
- GPU cuda 代码是否适用于多个 GPU 卡而无需任何实现
- std::copy_n 是否适用于重叠范围?