即使按值传递,也可以移动值

时间:2016-06-08 14:23:33

标签: c++

在以下程序中,如果参数按值传递,如何完成移动?

两个构造函数签名; MyContainer(Myclass&& myclass)MyContainer(Myclass myclass)工作得很好,但Myclass只有一个移动构造函数,其余的都被删除了。

#include <iostream>

class Myclass {
public:
    Myclass(int value) : value{value} {std::cout << "Constructed with value " << value << "\n";}
    Myclass(Myclass&& other) : value{other.value}
    {
        other.value = 0;
        std::cout << "Move constructed with value " << value << "\n";
    }
    Myclass(const Myclass& other) = delete;
    Myclass& operator=(const Myclass& other) = delete;
    Myclass& operator=(Myclass&& other) = delete;
    ~Myclass() {std::cout << "Destructed with value " << value << "\n";}
private:
    int value;
};

class MyContainer {
public:
    //MyContainer(Myclass&& myclass) : myclass{std::move(myclass)} { } // Shouldn't this be the constructor definition?
      MyContainer(Myclass   myclass) : myclass{std::move(myclass)} { } // this also works, why?
private:
    Myclass myclass;
};

int main()
{
    Myclass myclass{5};
    MyContainer mycontainer{std::move(myclass)};

    return 0;
}

当调用构造函数时,我确实使用std::move,但如果参数为Myclass而不是Myclass&&,编译器如何“记住”它被移动了?

3 个答案:

答案 0 :(得分:4)

在这两种情况下,您都将左值myclass转换为右值参考。

std::move的返回类型是一个右值引用,因此根据定义,std::move表达式是一个右值 - 因此,可以绑定到{{的移动构造函数的rvalue引用参数。 1}}。

MyClass指令可以解释为'我已完成此变量,现在可以移动它'。请注意,无论传递给std::move的变量的类型是什么,都是这种情况 - 它与正常变量,左值引用和右值引用一样。

答案 1 :(得分:3)

当您编写MyContainer mycontainer{std::move(myclass)};时,我们必须枚举MyContainer的所有可行构造函数,这些构造函数可以使用类型为Myclass的xvalue调用。 MyContainer有一个相关的构造函数,它接受Myclass类型的参数。为了查看该构造函数是否可行,我们在构造具有Myclass类型的xvalue的Myclass时再次有效地执行重载决策。这给了我们三个构造函数:

Myclass(int );                     // not viable: no conversion from `Myclass` to `int`
Myclass(Myclass&& );               // viable
Myclass(const Myclass& ) = delete; // viable

移动构造函数是可行的,因为您可以将rvalue引用绑定到xvalue。复制构造函数是可行的,因为您可以将const lvalue引用绑定到任何东西。重载分辨率中的一个破坏者是选择至少 cv - 限定参考。移动构造函数是Myclass的右值引用。复制构造函数是Myclass const的左值引用 - 它更符合cv,因此不太受欢迎。因此,我们选择移动构造函数。此构造函数不会被删除,因此调用格式正确。

这里重要的一点是,我们正在使用Myclass类型的 xvalue 进行构建(std::move()的作用是什么 - 它&#39; sa施放到xvalue)。如果我们使用左值(即简单MyContainer mycontainer(myclass))进行构造,那么只有复制构造函数才可行,但它被明确删除,因此整个表达式都是格式错误的。

  

编译器如何记住&#39;如果参数为Myclass而不是Myclass&&

,则会移动它

没有记住。在这两种情况下,我们都尝试从类型为Myclass的xvalue初始化构造函数中的参数。在前一种情况下,涉及调用Myclass的移动构造函数。在后一种情况下,这是一个直接的参考绑定。

答案 2 :(得分:2)

  

如果参数是按值传递的话,如何完成移动?

常规值(参数)变量可以通过move进行复制初始化。

因此,使用值构造函数,通过从传递的值移动来复制初始化参数,然后通过从参数移动来复制初始化成员。

使用rvalue引用构造函数,参数引用绑定到传递的值,并通过从引用的值移动来复制初始化成员。

  

编译器如何记住&#39;如果参数为Myclass而不是Myclass&&

,则会移动它

编译器不需要记住&#39;对象是否已被移动(或者是否由移动或其他方式构建)。