c – 在无法完成RVO的情况下,我们应该写`std :: move`吗?

众所周知,std :: move不应该应用于函数返回值,因为它可以阻止RVO(返回值优化).我感兴趣的是,如果我们确实知道RVO不会发生,我们应该怎么做.

这就是C 14标准所说的[12.8 / 32]

When the criteria for elision of a copy/move operation are met,but
not for an exception-declaration,and the object to be copied is
designated by an lvalue,or when the expression in a return statement
is a (possibly parenthesized) id-expression that names an object with
automatic storage duration declared in the body or
parameter-declaration-clause of the innermost enclosing function or
lambda-expression,overload resolution to select the constructor for
the copy is first performed as if the object were designated by an
rvalue. If the first overload resolution fails or was not performed,
or if the type of the first parameter of the selected constructor is
not an rvalue reference to the object’s type (possibly cv-qualified),
overload resolution is performed again,considering the object as an
lvalue. [ Note: This two-stage overload resolution must be performed
regardless of whether copy elision will occur. It determines the
constructor to be called if elision is not performed,and the selected
constructor must be accessible even if the call is elided. — end note
]

以下是Effective Modern C一书中的解释

The part of the Standard blessing the RVO goes on to say that if the
conditions for the RVO are met,but compilers choose not to perform
copy elision,the object being returned must be treated as an rvalue.
In effect,the Standard requires that when the RVO is permitted,
either copy elision takes place or std::move is implicitly applied to
local objects being returned

据我所知,当返回物体最初不能被省略时,它应被视为右值.在这些示例中,我们可以看到,当我们传递大于5的参数时,移动了对象,否则会被复制.当我们知道RVO不会发生时,这是否意味着我们应该明确地写std :: move?

#include <iostream>
#include <string>


struct Test
{
    test() {}

    Test(const Test& other)
    {
        std::cout << "Test(const Test&)" << std::endl;
    }

    Test(Test&& other)
    {
        std::cout << "Test(const Test&&)" << std::endl;
    }
};

Test foo(int param)
{
    Test test1;
    Test test2;
    return param > 5 ? std::move(test1) : test2;
}

int main()
{
    Test res = foo(2);
}

该程序的输出是Test(const Test&).

解决方法

您的示例中发生的事情与RVO无关,而与三元运算符无关?如果使用if语句重写示例代码,程序的行为将是预期的行为.将foo定义更改为:
Test foo(int param)
  {
  Test test1;
  Test test2;
  if (param > 5)
    return std::move(test2);
  else
    return test1;
  }

输出Test(Test&&).

如果你写(param> 5)会发生什么?std :: move(test1):test2是:

>三元运算符结果推导为prvalue [expr.cond]/5
>然后test2通过lvalue-to-rvalue转换,根据[expr.cond]/6的要求导致copy-initialization
>然后返回值的移动构造被省略[class.copy]/31.3

因此,在您的示例代码中,移动elision发生,然而在形成三元运算符的结果所需的复制初始化之后.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...