传递const std :: string&作为参数的日子已经过去了吗?

我听到Herb Sutter最近的一次谈话,他建议通过const & std::vectorstd::string在很大程度上没有了。 他建议编写一个如下的函数现在更可取:

std::string do_something ( std::string inval )
{
   std::string return_val;
   // ... do stuff ...
   return return_val;
}

我明白return_val在函数返回时是一个右值,因此可以使用移动语义来返回,这很便宜。 但是, inval仍然大于参考的大小(通常作为指针实现)。 这是因为std::string具有各种组件,包括一个指向堆的指针和一个用于短字符串优化的成员char[] 。 所以在我看来,通过引用仍然是一个好主意。

任何人都可以解释为什么赫布可能会这样说?


赫伯说他说的是因为这样的情况。

比方说,我有函数A调用函数B调用函数C A将一个字符串通过B传递给C A不知道或不关心C ; 所有A知道的是B 也就是说, CB的实现细节。

假设A被定义如下:

void A()
{
  B("value");
}

如果B和C通过const&获取字符串,则它看起来像这样:

void B(const std::string &str)
{
  C(str);
}

void C(const std::string &str)
{
  //Do something with `str`. Does not store it.
}

一切都很好。 你只是通过指针,不复制,不移动,每个人都很开心。 C接受一个const&因为它不存储字符串。 它只是使用它。

现在,我想做一个简单的改变: C需要将字符串存储在某处。

void C(const std::string &str)
{
  //Do something with `str`.
  m_str = str;
}

你好,复制构造函数和潜在的内存分配(忽略短字符串优化(SSO))。 C ++ 11的移动语义应该能够去除不必要的拷贝构造,对吗? A通过临时; 没有理由为什么C应该复制数据。 它应该只是泄露给它的东西。

除了它不能。 因为它需要一个const&

如果我改变C以按值取其参数,那只会导致B对该参数进行复制; 我什么也得不到。

所以,如果我刚刚通过str通过所有的功能由值,依靠std::move围绕洗牌的数据,我们就没有这个问题。 如果有人想坚持下去,他们可以。 如果他们不这样做,那好吧。

它更昂贵吗? 是; 移入一个值比使用引用更昂贵。 它比副本更便宜吗? 不适用于使用SSO的小字符串。 值得这样做吗?

这取决于你的用例。 你讨厌内存分配多少?


传递const std :: string&作为参数的日子已经过去了吗?

没有 。 许多人将这个建议(包括Dave Abrahams)超出了它所适用的范畴,并将其简化为适用于所有std::string参数 - 始终按值传递std::string对于任何和所有参数都不是“最佳实践”任意参数和应用程序,因为这些会谈/文章所关注的优化仅适用于有限的一组案例。

如果您要返回一个值,改变参数或者取值,那么按值传递可以节省昂贵的复制并提供语法上的便利。

与往常一样,在不需要副本的情况下,通过const引用传递会节省大量复制。

现在来看具体的例子:

然而,inval仍然比引用的大小大(通常作为指针实现)。 这是因为std :: string具有各种组件,包括一个指向堆的指针和一个用于短字符串优化的成员char []。 所以在我看来,通过引用仍然是一个好主意。 任何人都可以解释为什么赫布可能会这样说?

如果堆栈大小是一个问题(并且假设没有内联/优化), return_val + inval > return_val - IOW,可以通过在这里传递值来减少峰值堆栈的使用(注意:过度简化ABI)。 同时,通过const引用传递可以禁用优化。 这里的主要原因不是避免堆栈增长,而是为了确保可以在适用的地方执行优化。

通过const引用传递的日子还没有结束 - 规则比过去更加复杂。 如果性能很重要,那么根据您在实现中使用的细节,考虑如何传递这些类型是明智的。


这高度依赖于编译器的实现。

但是,这也取决于你使用什么。

让我们考虑下一个功能:

bool foo1( const std::string v )
{
  return v.empty();
}
bool foo2( const std::string & v )
{
  return v.empty();
}

这些功能在单独的编译单元中执行,以避免内联。 然后 :
1.如果你将这个函数传递给这两个函数,你将不会在性能上看到很大的差异。 在这两种情况下,都必须创建一个字符串对象
2.如果传递另一个std :: string对象, foo2将优于foo1 ,因为foo1将执行深度复制。

在我的PC上,使用g ++ 4.6.1,我得到了这些结果:

  • 通过引用变量:1000000000次迭代 - >经过时间:2.25912秒
  • 按值变化:1000000000次迭代 - >经过时间:27.2259秒
  • 通过参考文字:100000000次迭代 - >经过的时间:9.10319秒
  • 按值计算:100000000次迭代 - >经过时间:8.62659秒
  • 链接地址: http://www.djcxy.com/p/30727.html

    上一篇: Are the days of passing const std::string & as a parameter over?

    下一篇: Is infinite loop still undefined behavior in C++ if it calls shared library?