C++ C++;字符串连接优化

C++ C++;字符串连接优化,c++,c++11,stl,language-lawyer,stdstring,C++,C++11,Stl,Language Lawyer,Stdstring,查看这样一段代码(添加注释): 我想知道,在哪些情况下,std::string的operator+必须进行复制(在使用复制构造/分配的意义上,而不是复制的内部缓冲区,例如,如果应用SSO),以及实际复制的内容。快速浏览一下只会有部分帮助,因为它列出了12个不同的案例。我要求确认我对本页的理解: 案例1)复制lhs,然后将rhs复制到此副本的末尾 在C++98中,情况2)-5)从char/const char*参数构造一个临时字符串,然后生成情况1) 在C++11案例2)-5)中,临时字符串是从

查看这样一段代码(添加注释):

我想知道,在哪些情况下,
std::string的
operator+
必须进行复制(在使用复制构造/分配的意义上,而不是复制的内部缓冲区,例如,如果应用SSO),以及实际复制的内容。快速浏览一下只会有部分帮助,因为它列出了12个不同的案例。我要求确认我对本页的理解:

  • 案例1)复制lhs,然后将rhs复制到此副本的末尾
  • 在C++98中,情况2)-5)从
    char/const char*
    参数构造一个临时字符串,然后生成情况1)
  • 在C++11案例2)-5)中,临时字符串是从
    char/const char*
    argument,然后在案例6)或7)中得到结果
  • 在C++11案例6)-12)中,r值参数将使用
    insert/append
    进行变异,如果提供了
    char/const char*
    参数,则由于
    insert/append
    上的重载,不需要临时变量。在所有情况下,都会返回一个r值,以便于进一步链接。不制作任何副本(除了要在插入位置追加/插入的参数的副本)。可能需要移动字符串的内容
因此,类似上述示例的链应导致:2)->6)->11)->8),不复制任何lhs,而只是修改第一次操作(创建临时字符串)产生的r值缓冲区

因此,这似乎与
运算符+=
一样有效,只要
运算符+
至少在r值参数上使用。这是正确的吗?除非两个参数都是l值字符串,否则在C++11及以后的版本中,在
运算符+=
上使用
运算符+=
还有什么意义吗?

编译器还可以进行哪些优化?


编辑:澄清问题的意图。最初的部分仅涉及语言的细节(实现不受限制);最后一个问题是关于额外的优化。

字符串是一个相当不透明的对象:它持有一个内部字符缓冲区,并按照它想要的方式管理它。向字符串添加单个字符可能会导致分配新缓冲区、初始部分的副本和添加部分的副本。这一切都取决于分配的缓冲区是否足够大,以接受添加的部分

引文说:

。。。不制作任何副本(除了要在插入位置追加/插入的参数的副本)可能需要移动字符串的内容

换言之,新的分配,旧缓冲区的完整拷贝和释放

当您谈到效率和优化时,您必须记住编译器不必遵循您编写程序的方式。由于“仿佛”规则,只要尊重可观察的行为,它就可以优化它想要的方式。C++标准说:

1.9程序执行[简介执行]

5执行格式良好的程序的一致性实施应产生相同的可观察行为 作为使用相同程序的抽象机器的相应实例的可能执行之一 和相同的输入

一份说明甚至解释说:

一个实现可以自由地忽略本规范的任何要求 国际标准,只要结果是符合要求,就可以从 程序的可观察行为

因此,
a=a+b
a+=b是用完全相同的代码编译的


当编写C++程序时,你不应该担心低级优化:编译器会关心它,并且通常说编译器比你聪明。只有当您确定了真正的瓶颈时,才可以这样做,并且要注意,如果只针对一个体系结构和一个配置上的一个编译器进行低级别优化。

修复了我认为的打字错误。如果你的意思是<代码>某个FUNION()/Case>。实际上不是一个类型,但是我猜C++版本中你的版本更清楚。用<代码> G+++保存Times 编译,并在不同的优化级别查看结果汇编程序输出是有意义的。使用
-O3
它调用
string::reserve()
一次和
string::append()
4倍于您的代码。@G.Sliepen:更简单的是:使用“我认为诚实地说,我更关心的是在增加字符串时重新分配副本的性能,而不是按值返回,因为已确定的值很可能会得到相当好的优化。我不太关心效率,更关心语义,所以这是正确的,虽然对我来说并不陌生,但我很想念我的问题。我更关心调用函数的哪个版本,然后是现在调用的速度到底有多快。在这两者之间可能发生重新分配这一事实与imo的问题是正交的,因为除非您事先知道内容的大小和保留空间,“所以a=a+b;和a+=b;很可能是在完全相同的代码中编译的。”您确实检查过这一点吗?我非常怀疑任何编译器是否真的能够进行这种优化(除非
a
是临时的)。
a=a+b
在我看来至少需要额外的移动赋值。最佳情况(假设可能)编译器意识到a即将被重新分配,因此不再需要它的值,将其视为右值,这导致出现情况6),从而在r值上追加。然而,结果的r值仍然被分配给a,我看不到任何方法在这里应用rvo,因为append不会返回一个新对象。
std::string some_var;
std::string some_func(); // both are defined, but definition is irrelevant
...
return "some text " + some_var + "c" + some_func(); // intentionally "c" not 'c'