【问题标题】:Is it recommended to std::move a string into containers that is going to be overwritten?是否建议将字符串 std::move 到将被覆盖的容器中?
【发布时间】:2012-08-16 06:15:23
【问题描述】:

我有以下代码

std::vector<std::string> lines;
std::string currentLine;

while(std::getline(std::cin, currentLine)) {
  // // option 1
  // lines.push_back(std::move(currentLine));

  // // option 2
  // lines.push_back(currentLine);
}

我发现两者的成本不同

  1. 第一种方法将清除currentLine,使得getline 需要为字符串分配一个新的缓冲区。但它会改为使用向量的缓冲区。

  2. 第二种方法将使getline 能够重用缓冲区,并且需要为向量内字符串分配新的缓冲区。

在这种情况下,有没有“更好”的方法?编译器可以更有效地优化一种或其他方法吗?或者是否有聪明的字符串实现使一个选项比另一个选项更高效?

【问题讨论】:

  • 如果我要选择,我会选择选项二,因为currentLine(我认为)通常比它需要的大。 (副本不会分享capacity
  • 如果有的话,选项 2 允许 currentLine 增长,直到它大到足以容纳看到的最大值。如果看到的最大值不是最后看到的值,那么对std::getline 的额外调用将​​不需要重新分配currentLine 的缓冲区。
  • 如果性能是一个问题,我会使用move,然后将currentLine 调整为与刚刚移动的字符串相同的大小。
  • 除非你能真正快速打字,否则我认为这不会有任何不同。 :-) 开玩笑。我也喜欢2。在计算分配数量方面做得很好。

标签: c++ c++11 move-semantics


【解决方案1】:

鉴于短字符串优化的普遍性,我的直接猜测是,在许多情况下,这些都不会产生任何影响 - 使用 SSO,移动最终会复制包含的数据(即使源是右值,因此它有资格作为移动源)。

在你给出的两个版本之间,我想我倾向于支持不动的版本,但我怀疑无论哪种方式都会产生很大的不同。鉴于(大部分时间)您将在搬家后立即重新使用源,我怀疑搬家真的会做很多好事(即使充其量也是如此)。假设不涉及 SSO,您的选择是在向量中创建一个新字符串来保存您读取的字符串的副本,或者从您读取的字符串中移动并(本质上)创建一个新字符串来保存下一行下一次迭代。无论哪种方式,昂贵的部分(分配一个缓冲区来保存字符串,将数据复制到该缓冲区)都将几乎相同。

就“有没有更好的方法”而言,我至少能想到几种可能性。最明显的是内存映射文件,然后遍历该缓冲区,找到行尾,并使用emplace_back 直接从缓冲区中的数据在向量中创建字符串,根本没有中间字符串。

这确实有内存映射未标准化的小缺点——如果您无法忍受这种不可移植性,您可以将整个文件读入缓冲区而不是内存映射。

之后的下一个可能性是创建一个具有类似 const 字符串的接口的类,它只维护一个指向大缓冲区中数据的指针,而不是复制它(例如,CLang 使用类似的东西) .这通常会减少总分配、堆碎片等,但如果您(例如)需要在之后修改字符串,那么它不太可能(如果有的话)用处不大。

【讨论】:

  • 短字符串优化有多受欢迎?我不记得至少在 VC++ 中看到过它(还没有检查过最新版本或其他地方,tho)。
  • VC++ 做 SSO 已经有一段时间了。我相信使用 gcc 取决于您使用哪个库——有些使用它而另一些不使用(但我不太确定哪些确切的使用/不使用)。
  • VC++ 使用 SSO 已经有一段时间了,但无论我的 GCC 使用什么都没有。
猜你喜欢
  • 2020-07-30
  • 1970-01-01
  • 1970-01-01
  • 2013-09-16
  • 2020-09-26
  • 2014-08-30
  • 1970-01-01
  • 2015-03-28
  • 2013-05-21
相关资源
最近更新 更多