0

更新:对不起,我错了,二进制+实际上是左关联的。但尽管如此,这个问题仍然很重要,因为有一个补充问题。


我用 C/C++ 阅读,+运算符是右关联的。这意味着给定std::stringsa, b, c全部初始化为某个非零长度,

string d = a + b + c;

应该完全等同于(即产生相同的可执行文件)

string d = a + (b + c);

现在,这似乎是相当不幸的:扩展abyb然后 by会更有意义,c

string d = (a + b) + c;

因为这应该与b.size() + c.size()复制操作一起运行,因为不需要重新分配。

那么,我应该在实践中使用括号/单独+=分配还是规范允许进一步优化以使无括号版本同样有效?

我在这里主要关注 C++11,也将感谢(又一篇)关于移动语义等的帖子。如果这适用于此。

4

2 回答 2

5

虽然一元 operator+是右结合的(就一元运算符而言),但二元 operator+实际上是左结合的。

解决您的疑虑:

string d = a + b + c;

这首先将给出一个临时的结果(a+b)。暂时无法解决,因为 a 和 b 无法更改。临时将在内存中保留至少a.size()+b.size()字节,并将其内容a复制b到该新内存中。

然后c将被添加到临时调用operator+(string&& lhs,const string& rhs)中。这将获取临时文件并将其内容添加c到其中,可能是在重新分配以补偿对更多空间的需求并将a.size()+b.size()字节从旧内存复制到新内存之后。之后, 的内容c将附加到新内存中的临时文件中。

然后移动构造函数将启动,获取临时内存的所有权。

总共最多给出两个(重新)分配(每个中一个operator+),一个释放(在重新分配期间tmp + c)和四个副本(a、b、c 和重新分配期间的临时)。没有复制操作,因为字符串内容可以整体复制(memcpy/memmove),因为char数组是不需要一个接一个复制的POD a.size()+b.size()

这里唯一可以优化的是在执行时为最终结果临时保留足够的内存a+b。虽然从语言/库的角度来看,对此无能为力,但优化器理论上可以看到分配和条件重新分配,并通过从一开始就保留足够的内存来做正确的事情。我不知道实现这种优化有多容易,但我想检测类似的东西string z = a + b + c + d + e + f...并不容易,所以我不会指望它。

无论如何,临时变量通常不会损害您的程序性能,因为像这样的字符串操作很少发生在性能关键的地方。

如果您绝对必须提高该操作的性能,这是要走的路:

string d;
d.reserve(a.size() + b.size() + c.size());
d.append(a);
d.append(b);
d.append(c);

在储备调用期间导致一个分配,并且恰好是三个必要的副本。

于 2013-08-21T12:28:08.223 回答
2

您将一元加法与加法混淆。

于 2013-08-21T12:28:49.570 回答