[英]Accept move-only parameter by value or rvalue reference
The accepted answer of this post Pass by value vs pass by rvalue reference says that: 这个帖子的接受答案通过价值与通过右值参考传递说明:
For move-only types (as
std::unique_ptr
), pass-by-value seems to be the norm... 对于仅移动类型(如std::unique_ptr
),pass-by-value似乎是常态......
I'm a little bit doubtful about that. 我对此有点怀疑。 Let's say there is some non-copyable type, Foo
, which is also not cheap to move; 让我们说有一些不可复制的类型, Foo
,它也不便宜移动; and some type Bar
that has a member Foo
. 和一些有成员Foo
类型Bar
。
class Foo {
public:
Foo(const Foo&) = delete;
Foo(Foo&&) { /* quite some work */ }
...
};
class Bar {
public:
Bar(Foo f) : f_(std::move(f)) {} // (1)
Bar(Foo&& f) : f_(std::move(f)) {} // (2)
// Assuming only one of (1) and (2) exists at a time
private:
Foo f_;
};
Then for the following code: 然后为以下代码:
Foo f;
...
Bar bar(std::move(f));
Constructor (1) incurs 2 move constructions, while constructor (2) only incurs 1. I also remember reading in Scott Meyers's Effective Modern C++ about this but can't remember which item immediately. 构造函数(1)引发2个移动构造,而构造函数(2)只引发1.我还记得在Scott Meyers的Effective Modern C ++中读到这个但是不能立即记住哪个项目。
So my question is, for move-only types (or more generally, when we want to transfer the ownership of the argument), shouldn't we prefer pass-by-rvalue-reference for better performance? 所以我的问题是,对于仅移动类型(或者更一般地说,当我们想要转移参数的所有权时),我们是否应该更倾向于通过rvalue-reference来获得更好的性能?
UPDATE: I'm aware that the pass-by-value constructors/assignment operators (sometimes called unifying ctors/assignment operators) can help eliminate duplicate code. 更新:我知道pass-by-value构造函数/赋值运算符(有时称为统一 ctors /赋值运算符)可以帮助消除重复的代码。 I should say I'm more interested in the case when (1) performance is important, and (2) the type is non-copyable and so there are no duplicate ctors/assignment operators which accept const
lvalue reference arguments. 我应该说我对以下情况更感兴趣:(1)性能很重要,(2)类型是不可复制的,因此没有重复的ctors /赋值运算符接受const
左值引用参数。
UPDATE 2: So I've found Scott Meyers's blog about the specific problem: http://scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html . 更新2:所以我找到了Scott Meyers关于具体问题的博客: http : //scottmeyers.blogspot.com/2014/07/should-move-only-types-ever-be-passed.html 。 This blog discusses the reason that he advocates in Item 41 of his Effective Modern C++ that: 这篇博客讨论了他在有效现代C ++第41项中提倡的原因:
Consider pass by value only for copyable parameters ...that are cheap to move ...[and] always copied . 考虑仅为可复制参数传递值... 移动便宜 ...... [和] 总是被复制 。
There is an extensive discussion in that item about pass by value vs. rvalue reference, too much to be quoted here. 在该项目中有关于按值传递与右值参考的广泛讨论,这里引用太多。 The point is, both ways have their own advantages and disadvantages, but for transferring the ownership of a move-only object, pass by rvalue reference seems to be preferable. 关键是,两种方式都有各自的优点和缺点,但是为了转移仅移动对象的所有权,通过右值引用似乎更可取。
In this case we can have our cake and eat it. 在这种情况下,我们可以吃蛋糕然后吃。 A template constructor enabled only for Foo-like references gives us perfect forwarding plus a single implementation of a constructor: 仅为类似Foo的引用启用的模板构造函数为我们提供了完美的转发以及构造函数的单个实现:
#include <iostream>
#include <utility>
class Foo {
public:
Foo() {}
Foo(const Foo&) = delete;
Foo(Foo&&) { /* quite some work */ }
};
class Bar {
public:
template<class T, std::enable_if_t<std::is_same<std::decay_t<T>, Foo>::value>* = nullptr>
Bar(T&& f) : f_(std::forward<T>(f)) {} // (2)
// Assuming only one of (1) and (2) exists at a time
private:
Foo f_;
};
int main()
{
Foo f;
Bar bar(std::move(f));
// this won't compile
// Foo f2;
// Bar bar2(f2);
}
It's hard to imagine a class that's expensive to move: move semantics come exactly from the need to give a fast alternative to copies, when semantics allow. 很难想象一个移动成本昂贵的类:移动语义完全取决于语义允许时快速替换副本的需要。
You bring the example of std::string
and SSO. 您带来了std::string
和SSO的示例。 However that example is clearly flawed (I doubt they even turned on optimizations) because copying 16 bytes through memcpy
should take a bunch of CPU cycles since it can be implemented in 1 SIMD instruction to store them all at once. 然而,这个例子显然存在缺陷(我怀疑他们甚至开启了优化),因为通过memcpy
复制16个字节应该占用一堆CPU周期,因为它可以在1个SIMD指令中实现,以便一次性存储它们。 Also, MSVC 10 is really old. 此外,MSVC 10真的很老了。
So my question is, for move-only types (or more generally, when we want to transfer the ownership of the argument), shouldn't we prefer pass-by-rvalue-reference for better performance? 所以我的问题是,对于仅移动类型(或者更一般地说,当我们想要转移参数的所有权时),我们是否应该更倾向于通过rvalue-reference来获得更好的性能?
I shan't talk about performance, because it's such a peculiar aspect and can't be analyzed "in general". 我不会谈论性能,因为它是如此奇特的方面,不能“一般”分析。 We'd need concrete cases. 我们需要具体案例。 Also, compiler optimizations also needs to be considered; 此外,还需要考虑编译器优化; quite heavily, actually. 实际上相当重。 And not to forget a thorough performance analysis. 不要忘记彻底的性能分析。
std::unique_ptr
is a bit different because (i) can only be moved due to owning semantics (ii) it is cheap to move. std::unique_ptr
是有点不同的,因为(ⅰ)只能被移动,由于拥有语义(ii)其是便宜的移动。
My view. 我的观点。 I would say if you have to provide a "faster" alternative as an API, provide both - just like std::vector::push_back
. 我想说如果你必须提供一个“更快”的替代品作为API,提供两者 - 就像std::vector::push_back
。 It could have, as you say, slight improvements. 正如你所说,它可能会有轻微的改进。
Otherwise, even for move-only types, passing by const-reference still works and if you think it wouldn't, go for pass-by-value. 否则,即使对于仅移动类型,通过const-reference传递仍然有效,如果你认为它不会,则转换为pass-by-value。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.