#include <iostream>
class Class
{
public:
Class() { std::cerr << "ctor" << std::endl; }
~Class() { std::cerr <<"dtor" << std::endl; }
Class(Class&) { std::cerr << "copy ctor" << std::endl; }
Class & operator=(const Class &)
{
std::cerr << "copy operator=" << std::endl;
return *this;
}
Class(Class&&) { std::cerr << "move ctor" << std::endl;}
Class & operator=(Class &&)
{
std::cerr << "move operator="<< std::endl;
return *this;
}
};
int main(int, char**)
{
Class object;
Class && rvr = Class();
object = rvr; // (*)
}
输出
ctor
ctor
copy operator=
dtor
dtor
1)为什么在行(*)处调用“copy ctor”?
2)如果我必须每次都使用std :: move(),那么“移动语义”和任何移动数据的方法之间有什么区别,比如object.destructive_move();
?
3)当准确移动ctor / operator时?
谢谢!
答案 0 :(得分:6)
不在行(*)处调用复制ctor,调用复制赋值运算符。我猜这就是你的意思。
由于rvr
是左值,因此调用的是复制赋值运算符而不是移动赋值运算符。请注意,表达式的类型与它是否为左值是正交的。
如果您将赋值语句修改为object = Class();
甚至object = static_cast<Class&&>(rvr)
,您会发现调用了移动赋值运算符,因为在这两种情况下RHS是一个右值。
这种行为是明智的:比如考虑移动赋值运算符的实现。它的参数有rvalue类型引用,但它仍然是一个左值。如果它是一个rvalue,那么第一次使用该参数可以修改其状态以成为一个“空”对象(移动语义),然后第二次使用该参数可能无法按预期工作。
实际工作方式,当你想以一种让它处于“空”状态的方式使用参数时,你会明确地使用std::move
。
答案 1 :(得分:3)
Class && rvr = Class();
我要说的是没有意义,但这是C ++ 11规则。
rvr
是一个变量,它是对Class
的右值引用。这很容易理解。但理解这一点很重要:命名变量永远不是右值表达式。 即使它是右值参考!
同样,我知道这没有意义,但那是规则。右值引用变量不是右值表达式。
临时是一个右值表达式,因此Class()
会给你一个右值。从函数返回的临时值也是一个右值表达式,所以如果你有一些按值返回Class
的函数,你会得到一个右值。
这很重要,因为如果你有两个基于lvalue和rvalue引用重载的函数(比如复制/移动构造函数/赋值),如果表达式类型,C ++ 11将只选择rvalue引用版本 是一个左值。
这就是std::move
存在的原因。它存在以明确说明何时想要移动某些东西。它通过将您赋予它的值转换为右值表达式来实现。即,它返回Class&&
。
这是令人困惑的部分。如果您有一个命名的右值引用变量,那么它不是右值表达式。但是,如果您有一个未命名的右值引用,例如从函数返回Class&&
,则是一个右值表达式。
std::move
返回Class&&
。因此,如果您想从命名变量移动,则必须始终在其上调用std::move
。因此,如果您想从rvr
移动,则必须执行此操作:
object = std::move(rvr); // (*)
这会将Class&&
返回rvr
。将发生C ++ 11重载解析。由于operator=
的参数是rvalue表达式,因此它将选择operator=
的右值参考版本。从而引发一次行动。
请注意,std::move
实际上只是半复杂演员的包装。它没有做动;这是移动的operator=
。