13

我有以下代码

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. 第一种方法将 clear currentLine,使得getline需要为字符串分配一个新的缓冲区。但它将改为使用向量的缓冲区。

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

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

4

1 回答 1

3

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

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

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

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

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

于 2012-08-20T22:54:35.350 回答