“想要速度?通过价值”是多么真实

时间:2014-02-06 14:26:37

标签: c++ c++11

如果我错了,请纠正我。说我有:

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),他谈到“想要速度?通过价值”,我想知道是否有性能差异或通过引用传递参数(或字符串是精确的)并通过值传递“以获得速度?”

我知道按值传递参数可能很昂贵,特别是在处理大数据时。

也许(显然?)我的谈话中缺少一些东西?

3 个答案:

答案 0 :(得分:52)

"Want speed? Pass by value" (1)的想法是,有时,副本可以省略。参加课程XY,请考虑以下用例:

// 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());
}

现在让我们分析一下构造案例。

首先

Xfoo()返回一个临时值,用于初始化对象name。然后将该对象移至mem_name。请注意,编译器可以应用返回值优化,并在foo()的空间中直接构造operator+的返回值(实际上甚至是name的返回值 。因此,实际上不会发生复制,只是一次移动。

现在让我们分析Yfoo()再次返回一个临时值,该临时值绑定到引用name。现在返回值没有“外部提供的”空间,因此必须在自己的空间中构造并绑定到引用。然后将其复制到mem_name。所以我们正在做一个副本,没有办法解决它。

简而言之,结果是:

  • 如果传入左值,则XY会在初始化Xname时执行副本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 ++保证移动语义。