“想要速度? 按价值传递“
如我错了请纠正我。 说我有:
struct X { std::string mem_name; X(std::string name) : mem_name(std::move(name)) {} ... }; struct Y { std::string mem_name; Y(const std::string &name) : mem_name(name) {} ... };
在X
的ctor中, name
显然是传给X
的任何参数的拷贝, X
调用std::string
ctor来初始化mem_name
,对吗?
我们称之为复制 – 然后移动X * ; 两个操作: 复制,移动 。
在Y
的ctor中, name
是一个const ref,这意味着没有实际的元素副本,因为我们直接处理从Y
的对象需要创build的地方传递的参数。 但是,我们复制name
来初始化Y
mem_name
; 一个操作: COPY 。 当然,它应该快很多(对我来说更可取)?
在Scott Meyer的GN13演讲中(围绕时间8:10和8:56),他谈到了“想要速度?按价值传递” ,我想知道在传递参数(或string是否有任何性能差异或损失)通过参考和传递价值“为了获得速度?
我意识到通过价值传递参数可能会很昂贵,特别是在处理大量数据时。
也许(很明显?)我从谈话中失去了一些东西?
“想要速度?按价值传递” (1)的想法是,有时候,副本可以被消除。 考虑到你的类X
和Y
,考虑这个用例:
// Simulating a complex operation returning a temporary: std::string foo() { return "a" + std::string("b"); } struct X { std::string mem_name; X(std::string name): mem_name(std::move(name)) {} }; struct Y { std::string mem_name; Y(const std::string &name): mem_name(name) {} }; int main() { X(foo()); Y(foo()); }
现在让我们来分析两个施工案例。
X
先。 foo()
返回一个临时的,用来初始化对象的name
。 该对象然后移入mem_name
。 请注意,编译器可以直接在name
空间中应用Return Value Optimization并构造foo()
的返回值(实际上甚至是operator+
的返回值)。 所以没有复制实际发生,只有一个举动。
现在我们来分析一下Y
foo()
再次返回一个临时的参数name
。 现在没有“外部提供”的返回值空间,所以必须在自己的空间中构build并绑定到参考。 然后将其复制到mem_name
。 所以我们正在做一个副本,没有办法绕过它。
总之,结果是:
-
如果传递一个左值,则
X
和Y
都将执行一个副本(初始化name
时name
Y
,初始化mem_name
时name
Y
)。 另外,X
将执行一个移动(当初始化mem_name
)。 -
如果右值被传入,
X
可能只会执行一个移动,而Y
必须执行一个副本。
一般来说,一个移动可能是一个操作,其时间要求与传递一个指针的时间要求相当(这是通过引用做的)。 所以实际上,对于左值, X
不会比Y
更差,对于右值更好。
当然,这不是一个绝对的规则,必须用一粒盐。 如有疑问,简介。
(1)该链路很容易暂时不可用,截至2014年11月11日,似乎已经断开(404)。 内容的副本(虽然有奇怪的格式)似乎可以在几个博客网站:
- 在csdn.net上的博客
- blogspot.cz上的博客
另外,原始的内容可能通过backback机器访问。
还要注意,这个话题总体上引起了很大的讨论。 使用谷歌search引擎,带来了很多后续和对抗点。 列举其中一个例子,有“想要速度?不要(总是)通过值”由SO成员juanchopanza
没有通用规则。 通过价值传递可以在C ++ 11中获得一些重大胜利,包括移动语义和副本删除。
如果你真的想要速度,请剖析你的代码。
如果你真的不介意在你的API中暴露非引用(这应该是一个内部的标志,你将复制/分配给定的对象),那么使用副本是可以的。
复制elision比移动快,如果它不能被消除(由于各种原因,如调用过长的依赖函数调用链),C ++保证移动语义。