C ++ 11:当调用move ctor / operator =时?

时间:2012-07-01 16:50:36

标签: c++ c++11

#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时?

谢谢!

2 个答案:

答案 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=