#include <iostream>
struct Foo{
int i = 10;
};
Foo useless_move(Foo &&f){
return f;
}
int main() {
Foo f = Foo();
f.i = 100;
Foo f1 = useless_move(std::move(f));
std::cout << f.i;
std::cout << f1.i;
}
为什么我可以在使用Foo f1 = useless_move(std::move(f));
移动值后仍然可以访问f。我没有将所有权从f转移到f1吗?
答案 0 :(得分:3)
写std::move
并不会神奇地让行动发生;从那个意义上来说它的命名非常糟糕。它只是给你一个右值参考。
当您使用移动构造函数传递对象的rvalue引用时会发生移动,该移动构造函数被写入交换间接资源(如容器和文件句柄)而不是深度复制它们(或者根本无法正确复制它们)。
在这种情况下,您只有一个int
没有移动构造函数,没有移动,也没有移动。
而即使有,刚刚移出的对象的状态也未指定,不能保证......好吧,无论你想要的是什么。
答案 1 :(得分:2)
Foo::i
实际上是Foo的一部分,不能放弃。像你的胳膊一样。将资源从一个实例移动到另一个实例仅对指向对象所拥有的外部资源的指针有用:
#include <iostream>
struct Foo{
int* i; //pointer to external resource
Foo() :i(new int(10)) {}
Foo(const Foo& rhs) :i(new int(*(rhs.i))) {}
Foo(Foo&& rhs) :i(rhs.i) noexcept {rhs.i = nullptr;}
Foo& operator=(const Foo& rhs) {int* t=new int(*(rhs.i)); delete i; i=t; return *this;}
Foo& operator=(Foo&& rhs) noexcept {delete i; i=rhs.i; rhs.i=nullptr; return *this;}
~Foo() {delete i;}
};
此外,右撇子中存在极少数奇怪的怪癖,你又击中另一个:
Foo useless_move(Foo &&f){
return f;
}
int main() {
Foo f;
Foo f1 = useless_move(std::move(f));
}
您已将f
传递给std::move
,后者返回右值引用。这是对f
的引用,“注意”表示可以安全地从中移除。没有采取任何行动。然后将此引用传递给函数useless_move
,该函数接受右值引用。也就是说,它接受对Foo
的引用,并注意它可以安全移动。然后,它会在函数内部为此引用指定名称f
。没有采取任何行动。这是一个奇怪的部分:f
本身不是右值参考。这是lvalue
参考。声明中的&&
表示函数接受,但在函数内部,它被视为Foo&
。所以f
是函数内部的常规引用。因此,return f
加上返回类型Foo
会告诉函数返回从引用Foo
构建的f
,其中不是安全的搬离。所以它制作了一份副本。 仍然没有动作。最后,Foo f1 = ...
从返回值构造变量f1
。这一个(最后!)是一个举动(从副本,而不是从原始)。除了编译器可以检测到一个变量是从一个临时构造的,因此可能会移动,并且Foo
直接构造变量。所以只发生了理论上的举动。
答案 2 :(得分:1)
std::move()
不执行移动,它实际上是对rvalue-reference的强制转换。只有一个可以发生的移动,并且在Foo f = Foo()
行上,假设不进行优化。要实现移动,请按价值进行论证:
Foo useless_move(Foo f);
Foo f1 = useless_move(std::move(f));
此外,原始类型的移动是一个副本,所以你仍然不会看到任何差异。