如何通过右值引用返回?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (41)

一个C ++下一篇博客文章

A compute(…)
{
    A v;
    …
    return v;
}

如果A有一个可访问的副本或移动构造函数,编译器可能会选择删除该副本。否则,如果A有移动构造函数,v则移动。或者,如果A有复制构造函数,v则复制。否则,会发出编译时错误。

我认为我应该总是返回值,std::move 因为编译器能够找出用户的最佳选择。但在另一个来自博客文章的例子

Matrix operator+(Matrix&& temp, Matrix&& y)
  { temp += y; return std::move(temp); }

这里std::move是必须的,因为y必须把它作为函数内部的左值。

我尽力去理解推理,但我研究得越多,我就变得越困惑。为什么我们应该借助于这个std::move来返回这个值?

提问于
用户回答回答于

那么,假设你有:

A compute()
{
  A v;
  …
  return v;
}

你在做:

A a = compute();

这个表达式涉及两个传输(复制或移动)。首先表示的对象是v在函数中必须转移到函数的结果,即由compute()表情。我们把这叫做转移1。然后,将这个临时对象传输到创建a-调动2。

在许多情况下,可由编译器对象来选择传输1和2。v直接在a不需要转移。在本例中,编译器必须使用命名返回值优化来传输1,因为返回的对象是命名的。但是,如果禁用复制/移动省略,则每次传输都需要调用A的复制构造函数或移动构造函数。在大多数现代编译器中,编译器会看到v将被销毁,它将首先将其移动到返回值中。然后,这个临时返回值将被移动到a。如果A不具有移动构造函数,则将为两个传输复制它。

现在让我们看看:

A compute(A&& v)
{
  return v;
}

我们返回的值来自传递给函数的引用。编译器不只是假设v是暂时的,从IT 1移动是可以的。在这种情况下,传输1将是一个副本。那么Transfer 2将是一个移动--这是可以的,因为返回的值仍然是临时的(我们没有返回引用)。知道因为我们的参数是一个rvalue引用,所以我们可以显式地告诉编译器处理v作为临时的std::move:

A compute(A&& v)
{
  return std::move(v);
}

现在转移1和2都是移动。


编译器不自动处理v,定义为A&&,作为一个价值是安全的。想明白这一点并不是太愚蠢。一旦对象有了名称,它就可以在代码中多次引用。考虑:

A compute(A&& a)
{
  doSomething(a);
  doSomethingElse(a);
}

如果a会自动被视为一个rvalue,doSomething可以自由地把它的内脏挖出来,这意味着a被传递给doSomethingElse可能是无效的。即使doSomething如果按值计算它的参数,则对象将从其中移出,因此在下一行中无效。为了避免这个问题,命名rvalue引用是lvalue。这意味着doSomething被称为a在最坏的情况下,将被复制,如果不只是由lvalue引用-它将仍然有效在下一行。

这取决于compute要说,“好的,现在我允许从这个值中移出,因为我确信它是一个临时对象”。你这么做是因为你说std::move(a)。例如,你可以给doSomething一份副本,然后允许doSomethingElse离开它:

A compute(A&& a)
{
  doSomething(a);
  doSomethingElse(std::move(a));
}
用户回答回答于

函数结果的隐式移动只能用于自动对象。rvalue引用参数并不表示自动对象,因此在这种情况下必须显式请求移动。

所属标签

可能回答问题的人

  • 人生的旅途

    10 粉丝484 提问6 回答
  • 不吃貓的鱼oo

    5 粉丝466 提问6 回答
  • 富有想象力的人

    4 粉丝0 提问5 回答
  • Richel

    8 粉丝0 提问4 回答

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励