如果我错了,请纠正我。说我有:
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 *上调用 copy-then-move ;两个操作: COPY,MOVE 。
在Y
的ctor中,name
是一个const引用,这意味着没有该元素的实际副本,因为我们直接处理从Y
所在的地方传递的参数需要创建对象。但后来我们复制name
以初始化mem_name
中的Y
;一个操作: COPY 。因此,它应该更快(对我来说更好)?
在Scott Meyer的GN13演讲中(时间框架8:10和8:56),他谈到“想要速度?通过价值”,我想知道是否有性能差异或通过引用传递参数(或字符串是精确的)并通过值传递“以获得速度?”
我知道按值传递参数可能很昂贵,特别是在处理大数据时。
也许(显然?)我的谈话中缺少一些东西?
答案 0 :(得分:52)
"Want speed? Pass by value" (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
。请注意,编译器可以应用返回值优化,并在foo()
的空间中直接构造operator+
的返回值(实际上甚至是name
)的返回值 。因此,实际上不会发生复制,只是一次移动。
现在让我们分析Y
。 foo()
再次返回一个临时值,该临时值绑定到引用name
。现在返回值没有“外部提供的”空间,因此必须在自己的空间中构造并绑定到引用。然后将其复制到mem_name
。所以我们正在做一个副本,没有办法解决它。
简而言之,结果是:
如果传入左值,则X
和Y
会在初始化X
,name
时执行副本Y
初始化mem_name
)。此外,X
将执行移动(初始化mem_name
时)。
如果传入右值,X
可能只会执行移动,而Y
则必须执行副本。
通常,移动预计是一个操作,其时间要求与传递指针(通过引用传递的内容)相当。实际上,对于左值,X
并不比Y
更差,对于右值更好。
当然,这不是一个绝对的规则,必须采取一些盐。如有疑问,请参阅。
(1)该链接容易暂时不可用,并且自2014年12月12日起,它似乎被打破(404)。一些博客网站上似乎提供了内容的副本(尽管有奇怪的格式):
或者,可以通过wayback machine访问原始内容。
另请注意,该主题总体上引起了很多讨论。谷歌搜索文章标题带来了很多后续和反点。要列出其中一个示例,SO成员"Want speed? Don't (always) pass by value"
juanchopanza答案 1 :(得分:13)
没有优化的一般规则。按值传递可以在C ++ 11中获得一些大的胜利,包括移动语义和复制省略。
如果你真的想要速度,Profile你的代码。
答案 2 :(得分:4)
如果您真的不介意在API中公开非引用(这应该是内部您将要复制/分配给定对象的标志),那么使用副本就可以了。
复制省略比移动快,如果不能省略(由于各种原因,如依赖函数调用的调用链太长),那么C ++保证移动语义。