0

在下面的示例中,我想通过函数 foo 和 bar 移动“a”。为什么bar函数中“pp”的地址会发生变化?我不明白为什么。我期待它与 foo 中的“tt”相同,这与 main 中的“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;
}
4

2 回答 2

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存储桶“空”,并且在 y 中由 f 和 g 处理后的值;

考虑是否需要两个变量。也许你可以重复使用一个:

A x = ...;
f(g(x));

这里 x 已被 f 和 g 就地突变。实际上没有使用移动语义。

您还应该熟悉称为复制省略的东西,也称为 RVO 和 NRVO,它允许某些变量在某些情况下共享相同的地址(相同的存储桶)。

于 2013-09-27T02:50:06.710 回答
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)))因为临时变量已经是右值,因此就足够了。

于 2013-09-26T20:52:24.597 回答