其他分享
首页 > 其他分享> > c – 我应该返回一个右值引用(通过std :: move’ing)吗?

c – 我应该返回一个右值引用(通过std :: move’ing)吗?

作者:互联网

一个C++Next blog post

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.然后,转移此临时对象以创建由 – 转移2表示的对象.

在许多情况下,转换器1和2都可以被编译器省略 – 对象v直接在a的位置构造,并且不需要传输.在此示例中,编译器必须使用传输1的命名返回值优化,因为返回的对象已命名.但是,如果我们禁用复制/移动省略,则每次传输都需要调用A的复制构造函数或其移动构造函数.在大多数现代编译器中,编译器将看到v即将被销毁,它将首先将其移动到返回值中.然后将此临时返回值移动到.如果A没有移动构造函数,则会为两个传输复制它.

现在让我们来看看:

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

我们返回的值来自传递给函数的引用.编译器不只是假设v是临时的,并且可以从it1移动.在这种情况下,转移1将是一个副本.然后转移2将是一个移动 – 这没关系,因为返回的值仍然是临时的(我们没有返回引用).但是既然我们知道我们已经接受了一个可以移动的对象,因为我们的参数是一个右值引用,我们可以明确告诉编译器将v视为std :: move的临时对象:

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

现在转移1和转移2都将移动.

1编译器不自动将v(定义为A&&)作为右值处理的原因是安全性.想出来并不是太愚蠢.一旦对象具有名称,就可以在整个代码中多次引用它.考虑:

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

如果a被自动视为右值,doSomething可以自由地撕掉它的内容,这意味着传递给doSomethingElse的a可能是无效的.即使doSomething按值获取其参数,该对象也将从下一行中移出,因此无效.为了避免这个问题,命名的右值引用是左值.这意味着当调用doSomething时,最坏的意思是复制,如果不是仅仅通过左值引用 – 它仍然在下一行中有效.

计算机的作者应该说,“好吧,现在我允许移动这个值,因为我肯定知道它是一个临时对象”.你这样做是通过说std :: move(a).例如,您可以为doSomething提供一个副本,然后允许doSomethingElse从中移动:

A compute(A&& a)
{
  doSomething(a);
  doSomethingElse(std::move(a));
}

标签:stdmove,c,return,rvalue-reference
来源: https://codeday.me/bug/20190926/1821212.html