在以下程序中,如果参数按值传递,如何完成移动?
两个构造函数签名; 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&&
,编译器如何“记住”它被移动了?
答案 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;对象是否已被移动(或者是否由移动或其他方式构建)。