通过值vs通过右值引用 [英] Pass by value vs pass by rvalue reference

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

问题描述

我应该如何声明我的函数:

When should I declare my function as:

void foo(Widget w);

而不是

void foo(Widget&& w);

假设这是唯一的重载(如在中,我选择一个或另一个,不是两个,没有其他重载)。不涉及模板。假设函数 foo 需要拥有 Widget (例如 const Widget& 不是本讨论的一部分)。我对这些情况以外的任何答案不感兴趣。

Assume this is the only overload (as in, I pick one or the other, not both, and no other overloads). No templates involved. Assume that the function foo requires ownership of the Widget (e.g. const Widget& is not part of this discussion). I'm not interested in any answer outside the scope of these circumstances. See addendum at end of post for why these constraints are part of the question.

我的同事和我可以想出的主要区别是右值引用参数强制您明确关于副本。调用者负责进行显式复制,然后在需要复制时使用 std :: move 传递它。在传值的情况下,副本的成本是隐藏的:

The primary difference that my colleagues and I can come up with is that the rvalue reference parameter forces you to be explicit about copies. The caller is responsible for making an explicit copy and then passing it in with std::move when you want a copy. In the pass by value case, the cost of the copy is hidden:

    //If foo is a pass by value function, calling + making a copy:
    Widget x{};
    foo(x); //Implicit copy
    //Not shown: continues to use x locally

    //If foo is a pass by rvalue reference function, calling + making a copy:
    Widget x{};
    //foo(x); //This would be a compiler error
    auto copy = x; //Explicit copy
    foo(std::move(copy));
    //Not shown: continues to use x locally

除了强制人们明确地复制和改变在调用函数时获得多少语法糖,这些不同之处是什么?他们对界面有什么不同?

Other than that difference. Other than forcing people to be explicit about copying and changing how much syntactic sugar you get when calling the function, how else are these different? What do they say differently about the interface? Are they more or less efficient than one another?

我的同事和我已经考虑过的其他事情:

Other things that my colleagues and I have already thought of:


  • 右值引用参数意味着您可以移动参数,但不会强制它。可能在调用站点传递的参数将在其后的原始状态。它也可能的函数将吃/更改参数,而不曾调用移动构造函数,但假设,因为它是一个右值引用,调用者放弃了控制。传递价值,如果你搬到它,你必须假设一个移动发生;没有选择。

  • 假设没有elisions,单一的移动构造函数调用会被通过rvalue消除。

  • 编译器有更好的机会删除副本/随着值的移动。任何人都可以证实这种说法?最好有一个到gcc.godbolt.org的链接,显示从gcc / clang优化生成的代码,而不是标准中的一行。我尝试显示此信息可能无法成功地隔离行为: https://godbolt.org/g/4yomtt li>
  • The rvalue reference parameter means that you may move the argument, but does not mandate it. It is possible that the argument you passed in at the call site will be in its original state afterwards. It's also possible the function would eat/change the argument without ever calling a move constructor but assume that because it was an rvalue reference, the caller relinquished control. Pass by value, if you move into it, you must assume that a move happened; there's no choice.
  • Assuming no elisions, a single move constructor call is eliminated with pass by rvalue.
  • The compiler has better opportunity to elide copies/moves with pass by value. Can anyone substantiate this claim? Preferably with a link to gcc.godbolt.org showing optimized generated code from gcc/clang rather than a line in the standard. My attempt at showing this was probably not able to successfully isolate the behavior: https://godbolt.org/g/4yomtt

附录: 为什么我将这个问题限制得太多?

Addendum: why am I constraining this problem so much?


  • 没有重载 - 如果有其他重载,这将讨论pass by value vs一组重载,包括const引用和rvalue引用,在这一点上,过载的集合显然更有效率并赢了。这是众所周知的,因此不是很有趣。

  • 没有模板 - 我不感兴趣的转发参考如何适应图片。如果你有一个转发引用,你仍然调用std :: forward。转发引用的目标是传递你收到的东西。副本不相关,因为你只是传递一个左值。

  • foo 需要拥有 Widget (aka没有 const Widget& ) - 我们不是在谈论只读函数。如果函数是只读的,或者不需要拥有或延长 Widget 的生命周期,那么答案简单地变成 const Widget& / code>,这也是众所周知的,而不是很有趣。我也会告诉你为什么我们不想谈论重载。

  • No overloads - if there were other overloads, this would devolve into a discussion of pass by value vs a set of overloads that include both const reference and rvalue reference, at which point the set of overloads is obviously more efficient and wins. This is well known, and therefore not interesting.
  • No templates - I'm not interested in how forwarding references fit into the picture. If you have a forwarding reference, you call std::forward anyway. The goal with a forwarding reference is to pass things as you received them. Copies aren't relevant because you just pass an lvalue instead. It's well known, and not interesting.
  • foo requires ownership of Widget (aka no const Widget&) - We're not talking about read-only functions. If the function was read-only or didn't need to own or extend the lifetime of the Widget, then the answer trivially becomes const Widget&, which again, is well known, and not interesting. I also refer you to why we don't want to talk about overloads.

推荐答案


右值引用参数强制您明确关于副本。

The rvalue reference parameter forces you to be explicit about copies.

是,pass-by-rvalue-有一个点。

Yes, pass-by-rvalue-reference got a point.


右值引用参数意味着您可以移动参数,但不强制它。

The rvalue reference parameter means that you may move the argument, but does not mandate it.

是的,pass-by-value获得了一个点。

Yes, pass-by-value got a point.

std :: unique_ptr ),pass-by-value似乎是正常的(主要是你的第二点,第一点不适用)。

For move-only types (as std::unique_ptr), pass-by-value seems to be the norm (mostly for your second point, and first point is not applicable anyway).

对于具有复制/移动(如 std :: shared_ptr )的类型,我们可以选择与先前类型的一致性,

For types which has both copy/move (as std::shared_ptr), we have the choice of the coherency with previous types or force to be explicit on copy.

除非您希望保证没有不必要的副本,否则我将使用传递值作为一致性。

Unless you want to warranty there is no unwanted copy, I would use pass-by-value for coherency.

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

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