是否建议将字符串std::移动到将被覆盖的容器中



我有以下代码

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能够重用缓冲区,并需要为矢量内字符串分配新的缓冲区。

在这种情况下,有没有"更好"的方法?编译器能否更有效地优化其中一种或另一种方法?或者有没有聪明的字符串实现可以使一个选项比另一个选项更具性能?

考虑到短字符串优化的普遍性,我的直接猜测是,在许多情况下,这些都不会有任何区别——使用SSO,移动最终会复制包含的数据(即使源是一个右值,因此它有资格作为移动的源)。

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

至于:"有更好的方法吗",我至少能想到几个可能性。最明显的方法是对文件进行内存映射,然后遍历缓冲区,找到行的末尾,并使用emplace_back直接从缓冲区中的数据在向量中创建字符串,而不使用任何中间字符串。

这确实有一个小缺点,即内存映射没有标准化——如果你不能忍受这种不可移植性,你可以将整个文件读取到缓冲区中,而不是内存映射。

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

最新更新