繁体   English   中英

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

[英]Moving local variable into return value of different type

当从C ++中的函数返回值时,我们有copy elision和(Named)返回值优化,帮助我们创建更高效​​的代码。 简而言之,以下代码:

std::vector<int> make_vec_1(){
    std::vector<int> v;
    v.resize(1e6);
    return v;
}

导致静默移动或直接构造到返回值的目的地,而不是副本。 围绕此规则也意味着在返回时显式移动返回的对象实际上阻止了这些优化。

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

该版本可以防止RVO,如Scott Meyers的Effective Modern C ++ ,第25项中所述。


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

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)起初看起来像是一面红旗,但我也怀疑这是正确的做法。 返回一对向量的以下两个函数也是如此:

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,或者是否还有其他一些优势在我这里领先?

我是否正确,当类型不同时,最好进入返回值,但返回值可以从移动的局部变量构造移动? 我是否误解了NRVO,或者是否还有其他一些优势在我这里领先?

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

[class.copy.elision] (强调我的)

3在以下复制初始化上下文中,可能会使用移动操作而不是复制操作:

  • 如果return语句中的表达式是一个(可能带括号的)id-expression,它指定一个对象,该对象具有在最内层封闭函数或lambda表达式的body或parameter-declaration-clause中声明的自动存储持续时间 ,或者

  • 如果throw-expression的操作数是非易失性自动对象的名称(函数或catch子句参数除外),其范围不会超出最内层封闭try-block的末尾(如果有的话) ,

首先执行重载决策以选择副本的构造函数,就好像该对象是由rvalue指定的一样 如果第一个重载决策失败或未执行,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值。 [注意:无论是否发生复制省略,都必须执行此两阶段重载决策。 如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数。 - 结束说明]

这不取决于类型匹配,并且在完全(N)RVO未发生的情况下是回退行为。 因此,您可以通过在make_opt_vec_2显式移动来获得任何make_opt_vec_2

鉴于std::move要么是悲观还是完全多余,我认为最好不要在返回函数本地对象时这样做。

您要显式编写移动的唯一情况是,返回的表达式更复杂。 在这种情况下,你确实靠自己,而不是移动是一种潜在的悲观情绪。 所以在make_vec_pair_2 ,进入该对正确的做法。

这里的经验法则是不仅仅移动作为函数本地对象的id表达式。 否则,离开。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM