在下面的示例中,我想移动" a"通过函数foo和bar。为什么" pp"的地址改变吧功能?我不明白为什么。我期待它和" tt"在foo中,它与" a"相同。是主要的。
#include <iostream>
#include <utility>
struct A
{
int a_;
};
template<typename T>
A foo(T &&t)
{
auto &&tt = std::move(t);
tt.a_ -= 3;
std::cout << "tt=" << tt.a_ << "\t&tt=" << &tt << "\n";
return tt;
}
template<typename T>
A bar(T &&p)
{
auto &&pp = std::move(p);
pp.a_++;
std::cout << "pp=" << pp.a_ << "\t&pp=" << &pp << "\n";
return pp;
}
int main()
{
A a;
a.a_ = 12;
std::cout << "a=" << a.a_ << "\t&a=" << &a << "\n";
foo(std::move(a));
std::cout << "a=" << a.a_ << "\t&a=" << &a << "\n";
std::cout << "function chain=" << bar(std::move(foo(std::move(a)))).a_ << "\n";
std::cout << "a=" << a.a_ << "\t&a=" << &a << "\n";
return 0;
}
答案 0 :(得分:1)
有些观点:
移动对象仍然意味着有不同的实例具有不同的地址。如果移动实例,则意味着其内容以一种理想的方式从一个实例移动到另一个实例,而不仅仅是复制内容。在你的例子中,没有什么是真正感动的,但我认为你已经意识到这一点。
您从A
和foo
返回bar
的新实例。这是创建新实例的位置。您调用bar(std::move(foo(std::move(a))))
,内部foo(std::move(a))
返回一个新的临时值。然后将此临时文件传递给bar
。
请注意,bar(std::move(foo(std::move(a))))
不必要很长,bar(foo(std::move(a)))
就足够了,因为临时已经是左值。
答案 1 :(得分:1)
如果您将变量视为存储桶,并将值视为存储桶中包含的内容,则从一个变量移动到另一个变量在概念上将一个存储桶清空为另一个存储桶。
考虑到这一点,让我们看看你想做什么:
有两个功能:
A f(A);
A g(A);
这些函数都接受一个值并返回一个值,并产生副作用。
然后我们有两个变量:
A x, y;
你想做y = g(f(x))
。那就是你要将x的值移动到f的参数中,然后将其从f的返回值移开,然后将其移动到g的参数中,然后将其移动到y中。
您可以执行以下操作:
A& f(A& p) { p.do_stuff(); return p; }
A& g(A& p) { p.do_stuff(); return p; }
制作参数和返回类型引用。这与std::ostream
的工作方式相同。
然后你可以使用move对最终结果将其移动到y:
A x = ...;
A y = std::move(f(g(x));
这会使x
存储桶&#34;空白&#34;以及在y;
考虑是否需要两个变量。也许你可以重用一个:
A x = ...;
f(g(x));
这里x已经被f和g原位突变。实际上没有使用移动语义。
您还应该熟悉一些名为 copy elision 的东西,也称为RVO和NRVO,它允许某些变量在某些情况下共享相同的地址(同一个存储桶)。