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