gpt4 book ai didi

c++ - 有多少复制操作可以在 'string=string+s1' 和 'string+=s1' 之间移动语义保存?

转载 作者:太空狗 更新时间:2023-10-29 22:54:09 24 4
gpt4 key购买 nike

this Stack Overflow question (不幸的是,自从我开始研究这个问题以来,这个问题就被搁置了),有几个人提到“在现代 C++ 中”,由于移动语义,编译后的代码不需要为操作复制字符串 string = string + s1,给人的印象是使用现代 C++ 编译器,string = string + s1 可以和 string += s1 一样高效。我发现这种说法很可疑,但我在 C++03 的遗留世界中工作,仍然对移动语义知之甚少。 (这是我的工作,我不选择我们的编译器。)

string += s1的成本

使用操作string += s1,除非string 的缓冲区扩展超过其先前分配的容量,否则不需要新的分配,并且假设合理实现string 类,在 string += s1 操作中不会创建临时对象。假设结果的大小符合先前分配的容量,string += s1 中成本最高的部分是将 s1 的内容附加(复制)到末尾string 的原始内容使用先前分配但未使用的空间。另请注意,该复制操作的成本只是 s1 的字节数,而不是总字节数。

旧版 C++(C++03 及更早版本)中 string = string + s1 的成本

在旧版 C++(03 及更早版本)中,string = string + s1 据我所知,至少需要一次临时分配(用于评估 string + s1), 以及s1 和原始string 中字节数之和的两份完整拷贝(1.复制的原始内容code>string 到临时文件并将 s1 的字节复制附加到临时文件中那些字节的末尾,然后 2. 复制所有的从临时返回到 string 缓冲区的结果字节,包括无论如何已经存在的原始内容的字节)。显然,这比上面描述的 string += 1 的成本要昂贵得多,并且它可能会产生显着差异,特别是如果在循环中多次执行追加操作(它是 Shlemiel the painter algorithm ,除了它甚至比 strcat() 的低效率还要糟糕!)。

“现代”C++(C++11(或 C++14?)及更高版本)中 string = string + s1 的成本

在我看来,表达式 string + s1 的计算将产生一个右值,随后可以将其作为对 string 的移动赋值的右值引用提供运算符,无需将 string + s1 的结果复制回 string。但这并不能消除创建原始临时对象以及相关联的复制的需要,不是吗?在我看来,移动语义所能做的最好的事情就是消除两个完整复制操作之一。仍然需要一次分配和一次复制操作(以创建临时文件),对吗?如果是这样,那么移动语义只会使这样的代码“不那么糟糕”,它不会阻止它成为 Shlemiel the painter algorithm 的另一个实例。 ,而且比C的strcat()还差!

请告诉我我错了。 另外:请不要猜测。那没有用。如果您的回答或评论以“我认为...”开头,请不要提交。

最佳答案

One allocation and one copy operation (to create the temporary) is still required, right? If so, then move semantics only makes code like that "less bad"

据我所知,你是对的。移动语义并没有消除这个例子中的所有开销,只是一些。

如果string 没有足够的容量,那么就无法避免所有字符的重新分配和复制。但是,如果有足够的容量,为了获得最佳性能,无论标准版本如何,您应该做的是:

string.append(s1); // this
string += s1; // or this

如果容量足够,则不会发生重新分配,并且不会复制原始 string 的字符。与 strcat 相同。与 strcat 不同,如果容量不足,行为是明确定义的。


在循环中进行时,理想情况下应在循环之前预留全部容量。如果无法知道结果长度,那么有一个潜在的问题:据我所知,标准没有指定 append 的容量增长策略。如果它做几何增长,那么带有追加的简单循环是好的。如果它只分配完全相同的旧长度 + 追加长度而没有空间开销,那么琐碎的追加循环将导致在每次迭代中都进行分配。

我测试了libstdc++,它呈几何级数增长。如果您偏执,那么为了保证这一点,您在技术上需要明确检查每次迭代是否会发生重新分配,并保留几何增长的内存量:

// paranoid mode: enabled
int growth_rate = 2;
for(auto& s1 : range) {
auto new_size = string.size() + s1.size();
auto new_cap = string.capacity();
while (new_cap < new_size)
new_cap *= growth_rate;
string.reserve(new_cap);
string += s1;
}

然而,在这种特殊情况下,std::ostringstream 可能更简单。

关于c++ - 有多少复制操作可以在 'string=string+s1' 和 'string+=s1' 之间移动语义保存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57151379/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com