首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将局部变量移到不同类型的返回值中

将局部变量移到不同类型的返回值中
EN

Stack Overflow用户
提问于 2019-06-06 19:42:08
回答 1查看 357关注 0票数 7

当从C++中的函数返回值时,我们提供了复制省略和(命名)返回值优化,帮助我们创建更高效的代码。简言之,以下代码:

代码语言:javascript
复制
std::vector<int> make_vec_1(){
    std::vector<int> v;
    v.resize(1e6);
    return v;
}

结果是无声地移动或直接构造到返回值的目的地,而不是副本。这方面的规则还意味着,返回时显式移动返回的对象实际上会阻止这些优化。

代码语言:javascript
复制
std::vector<int> make_vec_2(){
    std::vector<int> v;
    v.resize(1e6);
    return std::move(v); // BAD
}

此版本防止RVO,如Scott的“有效现代C++”第25项中所解释的那样。

我的问题是,当返回类型不同,但可以从一个或多个局部变量移动时,会发生什么?考虑以下函数,每个函数都返回一个可选向量:

代码语言:javascript
复制
std::optional<std::vector<int>> make_opt_vec_1(){
    std::vector<int> v;
    v.resize(1e6);
    return v; // no move
}

std::optional<std::vector<int>> make_opt_vec_2(){
    std::vector<int> v;
    v.resize(1e6);
    return std::move(v); // move
}

其中哪一个是正确的?在我看来,行return std::move(v)一开始就像是一个危险的标志,但我也怀疑这是正确的做法。对于返回一对向量的以下两个函数也是如此:

代码语言:javascript
复制
std::pair<std::vector<int>, std::vector<int>> make_vec_pair_1(){
    std::vector<int> v1, v2;
    v1.resize(1e6);
    v2.resize(1e6);
    return {v1, v2}; // no move
}

std::pair<std::vector<int>, std::vector<int>> make_vec_pair_2(){
    std::vector<int> v1, v2;
    v1.resize(1e6);
    v2.resize(1e6);
    return {std::move(v1), std::move(v2)}; // move
}

在这种情况下,尽管乍一看看起来很奇怪,但我认为进入返回值是更好的做法。

当类型不同时,移到返回值中更好,但返回值可以从要移出的局部变量中移动,对吗?我是否误解了NRVO,还是这里还有其他的优化在我前面?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-06-06 19:55:18

当类型不同时,移到返回值中更好,但返回值可以从要移出的局部变量中移动,对吗?我是否误解了NRVO,还是这里还有其他的优化在我前面?

你确实错过了一件事。即使类型不同,也会自动进行隐式移动。

class.copy.elision (重点地雷) 3.在以下副本初始化上下文中,可以使用移动操作而不是复制操作:

  • 如果返回语句中的表达式是一个(可能是括号大小的)id-表达式,该表达式将在最内部封闭函数或lambda表达式的正文或参数声明子句中声明的具有自动存储持续时间的对象命名,或
  • 如果抛出表达式的操作数是一个非易失性自动对象的名称(函数或catch子句参数除外),则其范围不会扩展到最内部封闭的try-块的末尾(如果有),

首先执行重载解析以选择副本的构造函数,就好像该对象是由rvalue指定的一样。如果第一个重载解析失败或没有执行,或者所选构造函数的第一个参数的类型不是对对象类型(可能是cv限定的)的rvalue引用,则将再次执行重载解析,将对象视为lvalue。 注意:无论复制是否会发生,这种两阶段过载解决方案都必须执行.如果不执行省略,它将确定要调用的构造函数,即使调用被省略,所选的构造函数也必须是可访问的。 - 尾注 

这不取决于类型匹配,并且是在不发生full (N)RVO的情况下的回退行为。因此,在make_opt_vec_2中显式移动不会获得任何好处。

考虑到std::move要么是悲观的,要么是完全多余的,我认为最好在简单地返回函数本地对象时不要这样做。

唯一想要显式写入移动的情况是,返回的表达式更加复杂。在这种情况下,你确实是一个人,不动是一种潜在的悲观。因此,在make_vec_pair_2中,移动成对是正确的。

这里的经验法则是不要只移动一个id表达式,它是一个函数本地对象。否则,走开。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56484125

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档