Per*_*ter 6 c++ move-semantics c++11
#include <iostream>
#include <time.h>
class A
{
public:
A() { std::cout << "a ctor\n"; }
A(const A&) { std::cout << "a copy ctor\n"; }
A(A&&) { std::cout << "a move ctor\n"; }
};
A f(int i)
{
A a1;
return i != 0 ? a1 : A{};
}
int main()
{
srand(time(0));
f(rand());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
演员
复印机
我预计A1在F()将被移动不会被复制。如果我稍微改变f(),它就不再是一个副本而是一个移动:
A f(int i)
{
A a1;
if (i != 0)
{
return a1;
}
return A{};
}
Run Code Online (Sandbox Code Playgroud)
输出是:
演员
一个动作演员
你能解释一下这是如何工作的吗?海湾合作委员会 9.3.0
(编辑:添加随机数以防止 RVO)
您看到的差异是由于使用了三元/条件运算符。三元运算符确定其第二个和第三个操作数的公共类型和值类别,这是在编译时确定的。看这里。
在你的情况下:
return i != 0 ? a1 : A{};
Run Code Online (Sandbox Code Playgroud)
公共类型是A,公共值类别是纯右值,因为A{}是纯右值。然而,a1是一个左值,并且在左值到右值的转换中必须创建它的纯右值临时副本。这解释了为什么您会看到在条件为 true 时调用复制构造函数:创建 的副本a1以将其转换为纯右值。纯右值被编译器复制删除。
在第二个示例中,您有一个if语句,这些规则不像三元运算符那样适用。所以这里没有调用复制构造函数。
为了解决您对第二个和第三个操作数具有左值的条件语句的评论,根据复制省略规则,如果满足以下条件,则允许:
在 return 语句中,当操作数是具有自动存储持续时间的非易失性对象的名称时,该对象不是函数参数或 catch 子句参数,并且与以下类型具有相同的类类型(忽略 cv 限定):函数返回类型。这种复制省略的变体称为 NRVO,“命名返回值优化”。
像这样的条件语句
return i != 0 ? a1 : a1;
Run Code Online (Sandbox Code Playgroud)
其中第二个和第三个操作数是左值,不满足此条件。该表达式是条件,而不是自动对象的名称。因此没有复制省略并且调用复制构造函数。