通过右值引用返回 [英] return by rvalue reference

查看:153
本文介绍了通过右值引用返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到C ++的代码接下来,博客说,

I saw the codes from C++ Next, the blog said that

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



如果A有一个可访问的复制或移动构造函数,编译器可以选择elide复制
否则,如果A有一个移动构造函数,v移动
否则,如果A有一个复制构造函数,v复制
否则会产生编译时错误。

If A has an accessible copy or move constructor, the compiler may choose to elide the copy otherwise, if A has a move constructor, v is moved otherwise, if A has a copy constructor, v is copied otherwise, a compile time error is emitted.

我认为我应该总是返回没有std :: move
的值,因为编译器能够找出用户的最佳选择
但是其他来自博客的示例告诉我们

I thought I should always return the value without std::move because the compiler would be able to figure out the best choice for users But the other examples from the blog tell us

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

这里的move是必须的,因为y必须在函数内部被视为一个左值。

Here the "move" is necessary because y must be treated as an lvalue inside the function.

啊,我的头几乎在学习这个博客(C ++下一页)后爆炸,我尽力研究他们的回复
,但我学习的越多,更混乱我成了。
为什么我们要在st :: move?的帮助下返回值。

Ah, my head almost blow up after studying this blog(C++ Next), I try my best to study their reply but the more I study, the more confuse I become. Why should we return the value with the help of st::move?

推荐答案

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

您正在:

A a = compute();

此表达式涉及两个转移(复制或移动)。首先,函数中 v 表示的对象必须传递给函数的结果,即 compute() expression。让我们称之为Transfer 1.然后,这个临时对象被传递来创建由 a - Transfer 2表示的对象。

There are two transfers (copy or move) that are involved in this expression. First the object denoted by v in the function must be transferred to the result of the function, i.e. the value donated by the compute() expression. Let's call that Transfer 1. Then, this temporary object is transferred to create the object denoted by a - Transfer 2.

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

In many cases, both Transfer 1 and 2 can be elided by the compiler - the object v is constructed directly in the location of a and no transferring is necessary. The compiler has to make use of Named Return Value Optimization for Transfer 1 in this example, because the object being returned is named. If we disable copy/move elision, however, each transfer involves a call to either A's copy constructor or its move constructor. In most modern compilers, the compiler will see that v is about to be destroyed and it will first move it into the return value. Then this temporary return value will be moved into a. If A does not have a move constructor, it will be copied for both transfers instead.

现在让我们看看:

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

我们返回的值来自传递给函数的引用。编译器不仅仅假设 v 是一个临时变量,并且可以从 1 移动它。在这种情况下,传输1将是副本。然后Transfer 2将是一个移动 - 没关系,因为返回的值仍然是一个临时的(我们没有返回一个引用)。但由于我们知道我们已经移动了一个对象,因为我们的参数是一个右值引用,我们可以明确告诉编译器处理 v 作为临时 std :: move

The value we're returning comes from the reference being passed into the function. The compiler doesn't just assume that v is a temporary and that it's okay to move from it1. In this case, Transfer 1 will be a copy. Then Transfer 2 will be a move - that's okay because the returned value is still a temporary (we didn't return a reference). But since we know that we've taken an object that we can move from, because our parameter is an rvalue reference, we can explicitly tell the compiler to treat v as a temporary with std::move:

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

现在Transfer 1和Transfer 2都会移动。

Now both Transfer 1 and Transfer 2 will be moves.

1 编译器不会自动处理 v ,定义为 A&&& ,因为右值是安全的。这不是太愚蠢的想出来。一旦一个对象有一个名称,它可以在整个代码中被引用多次。考虑:

1 The reason why the compiler doesn't automatically treat v, defined as A&&, as an rvalue is one of safety. It's not just too stupid to figure it out. Once an object has a name, it can be referred to multiple times throughout your code. Consider:

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

如果 a 自动处理为右值, doSomething 可以自由地剥离其内部,意味着 a 被传递给 doSomethingElse 可能无效。即使 doSomething 按值取其参数,对象也将从下一行中移出,因此无效。为了避免这个问题,命名的右值引用是左值。这意味着当调用 doSomething 时, a 将最坏的情况下复制,如果不是只由lvalue引用

If a was automatically treated as an rvalue, doSomething would be free to rip its guts out, meaning that the a being passed to doSomethingElse may be invalid. Even if doSomething took its argument by value, the object would be moved from and therefore invalid in the next line. To avoid this problem, named rvalue references are lvalues. That means when doSomething is called, a will at worst be copied from, if not just taken by lvalue reference - it will still be valid in the next line.

compute 的作者说:好吧,现在我允许这个值被移动,因为我知道它是一个临时对象。你可以这样做: std :: move(a)。例如,您可以给 doSomething 一个副本,然后允许 doSomethingElse 从中移动:

It is up to the author of compute to say, "okay, now I allow this value to be moved from, because I know for certain that it's a temporary object". You do this by saying std::move(a). For example, you could give doSomething a copy and then allow doSomethingElse to move from it:

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

这篇关于通过右值引用返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆