Rom*_*Coo 9 c++ copy return move c++11
假设我有一个MyClass带有正确的move构造函数的类,并且该类的副本构造函数被删除了。现在,我将像这样返回此类:
MyClass func()
{
return MyClass();
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,返回类对象时将调用move构造函数,并且一切都会按预期进行。
现在,假设MyClass有一个<<操作符的实现:
MyClass& operator<<(MyClass& target, const int& source)
{
target.add(source);
return target;
}
Run Code Online (Sandbox Code Playgroud)
当我更改上面的代码时:
MyClass func()
{
return MyClass() << 5;
}
Run Code Online (Sandbox Code Playgroud)
我收到编译器错误,因为复制构造函数被删除,因此无法访问它。但是为什么在这种情况下完全使用了复制构造函数呢?
Lig*_*ica 15
现在,我通过左值返回此类,如下所示:
Run Code Online (Sandbox Code Playgroud)MyClass func() { return MyClass(); }
不,返回的表达式是一个xvalue(一种rvalue),用于初始化按值返回的结果(自C ++ 17起,情况有些复杂,但这仍然是要点);此外,您使用的是C ++ 11)。
在这种情况下,返回类对象时将调用move构造函数,并且一切都会按预期进行。
确实; 一个右值将初始化一个右值引用,因此整个事物可以与move构造函数匹配。
当我更改上面的代码时:
…现在的表达式是MyClass() << 5,其类型为MyClass&。这绝不是一个右值。这是一个左值。这是一个引用现有对象的表达式。
因此,如果没有显式的std::move,它将用于复制初始化结果。而且,由于您的副本构造函数已删除,因此无法正常工作。
我很惊讶该示例完全可以编译,因为不能使用临时方法来初始化左值引用(您的运算符的第一个参数),尽管已知某些工具链(MSVS)可以接受此扩展。
那会回去
std::move(MyClass() << 5);工作吗
是的,我相信。
但是,这看起来很奇怪,并使读者重新检查以确保没有悬挂的引用。这表明有一种更好的方法可以完成此过程,从而使代码更清晰:
MyClass func()
{
MyClass m;
m << 5;
return m;
}
Run Code Online (Sandbox Code Playgroud)
现在您仍然可以采取行动(因为在return输入局部变量时这是一条特殊的规则),并且没有任何奇怪的滑稽动作。而且,作为奖励,该<<电话完全符合标准。
您的操作员将按返回MyClass&。因此,您返回的是左值,而不是可以自动移动的右值。
您可以依靠有关NRVO的标准保证来避免复制。
MyClass func()
{
MyClass m;
m << 5;
return m;
}
Run Code Online (Sandbox Code Playgroud)
这将使对象完全消失或移动。全部归因于它是一个函数局部对象。
当您尝试调用operator<<右值时,另一种选择是提供处理右值引用的重载。
MyClass&& operator<<(MyClass&& target, int i) {
target << i; // Reuse the operator you have, here target is an lvalue
return std::move(target);
}
Run Code Online (Sandbox Code Playgroud)
这将使MyClass() << 5自身结构良好(请参阅其他答案以了解为什么不是这样),并返回一个可以构造返回对象的xvalue。尽管这样和超载operator<<并不常见。