Joh*_*itb 13 c++ move-semantics c++11
我有以下代码
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);
}
Run Code Online (Sandbox Code Playgroud)
我看到两者的成本不同
第一种方法将清除currentLine,因此getline需要为字符串分配新的缓冲区.但它会使用缓冲区代替矢量.
第二种方法将getline能够重用缓冲区,并且需要为向量内字符串分配新的缓冲区.
在这种情况下,是否有"更好"的方式?编译器能否更有效地优化一种或另一种方法?或者是否有巧妙的字符串实现使一种选择方式比另一种方式更高效?
鉴于短字符串优化的盛行,我的直接猜测是,在许多情况下,这一切都不会产生任何影响 - 使用 SSO,移动最终会复制包含的数据(即使源是右值,所以它是有资格作为移动的来源)。
在你给出的两个版本之间,我想我更喜欢不动的版本,但我怀疑这两种方式都会产生很大的差异。考虑到(大多数时候)您将在移动后立即重新使用源代码,我怀疑移动是否真的会带来很多好处(即使是最好的情况)。假设不涉及 SSO,您的选择是在向量中创建一个新字符串来保存您读取的字符串的副本,或者从您读取的字符串中移动并(实质上)创建一个新字符串来保存下一行下一次迭代。无论哪种方式,昂贵的部分(分配一个缓冲区来保存字符串,将数据复制到该缓冲区)将几乎相同。
至于“有没有更好的方法”,我至少可以想到几种可能性。最明显的方法是对文件进行内存映射,然后遍历该缓冲区,找到行尾,并emplace_back直接从缓冲区中的数据在向量中创建字符串,根本没有中间字符串。
这确实有一个小缺点,即内存映射没有标准化——如果您不能忍受这种程度的不可移植性,您可以将整个文件读入缓冲区而不是内存映射。
之后的下一个可能性是创建一个具有像 const string 这样的接口的类,它只维护一个指向大缓冲区中数据的指针,而不是复制它(例如,CLang 使用类似的东西)。这通常会减少总分配、堆碎片等,但如果您(例如)随后需要修改字符串,则它不太可能有多大(如果有的话)用处。