我何时应该将函数声明为:
void foo(Widget w);
相对于
void foo(Widget&& w);
?
假设这是唯一的重载(例如,我选择一个或另一个,而不是两个,也没有其他重载)。不涉及模板。假设该功能foo
需要拥有Widget
(例如const Widget&
,这不是本讨论的一部分)。在这些情况范围之外,我对任何答案都不感兴趣。有关为什么这些约束是问题的一部分,请参见文章末尾的附录。
我和我的同事可以想到的主要区别是,右值引用参数迫使您对副本明确。调用方负责制作显式副本,然后在std::move
需要复制时将其传递给它。在按值传递的情况下,副本的成本被隐藏:
//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
除了那个区别。除了强迫人们明确地复制和更改调用函数时获得多少语法糖外,这些还有什么不同?他们对界面有何不同看法?它们是彼此相比效率更高还是更低?
我和我的同事已经想到的其他事项:
- 右值引用参数表示您可以移动参数,但不强制使用该参数。之后,您在调用站点传递的参数可能会保持其原始状态。该函数也有可能在不调用move构造函数的情况下吃掉/更改了参数,但假定由于它是一个右值引用,因此调用者放弃了控件。通过价值传递,如果您进入其中,则必须假设发生了移动;别无选择。
- 假设没有省略,通过rvalue传递消除了单个move构造函数调用。
- 编译器有更好的机会按值传递副本/移动。谁能证实这一说法?最好有一个指向gcc.godbolt.org的链接,该链接显示了来自gcc / clang的优化生成的代码,而不是标准中的一行。我试图证明这一点可能无法成功隔离行为: https //godbolt.org/g/4yomtt
附录: 为什么我要限制这么多的问题?
- 没有重载-如果还有其他重载,则将讨论按值传递与一组重载(包括const引用和rvalue引用)的重载的讨论,此时重载的集合显然更有效并且更成功。这是众所周知的,因此并不有趣。
- 没有模板-我对转发引用如何适合图片不感兴趣。如果您有转发参考,则无论如何都要调用std :: forward。具有转发参考的目标是在您收到邮件时传递它们。副本无关紧要,因为您只是传递了一个左值。这是众所周知的,并不有趣。
foo
需要拥有Widget
(aka noconst Widget&
)的所有权-我们不是在谈论只读功能。如果该函数是只读的,或者不需要拥有或延长的寿命Widget
,那么答案就变成了const Widget&
,这又是众所周知的,而且没有意思。我也向您介绍为什么我们不想谈论重载。
std::move
而不是制作中间副本呢?