这就是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&).
解决方法
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发生,然而在形成三元运算符的结果所需的复制初始化之后.