为啥 std::string append 在 rval ref 上没有重载?

Posted

技术标签:

【中文标题】为啥 std::string append 在 rval ref 上没有重载?【英文标题】:Why std::string append is not overloaded on rval ref?为什么 std::string append 在 rval ref 上没有重载? 【发布时间】:2020-11-10 16:08:26 【问题描述】:

我很熟悉,std::string 中的append 返回std::string&,因此它不符合被移动的条件,因此结果不会被移动。

#include <string>


int main()

    std::string s = std::string("A").append("B");
    return s.size();

https://godbolt.org/z/M63aWW

#include <string>


int main()

    std::string s = std::move(std::string("A").append("B"));
    return s.size();

https://godbolt.org/z/jTnsac

您可以看到,后一个示例将产生更少的分配,因此在这种情况下,最好移动一些看起来像临时的东西。我的问题是为什么他们(委员会)不根据上下文在&amp;&amp; 上添加简单的重载以使append 的结果为std::string&amp;std::string&amp;&amp;?我的意思是类似于std::optional 的事情正在处理value。有没有例子可以证明这种优化是假的?

【问题讨论】:

你能发布一个实际有用的例子吗?自动 res = std::string("A").append("B");剂量真的很有意义。 @Cortex 是的,这没有意义,但是当您尝试连接几个字符串时,可能会非常大,您希望尽可能少地重新分配。而append 将重用缓冲区,因此总体分配会更少。 解决方法:std::string s = std::move(std::string("A").append("B")); 但对所有这一切都很重要。这不是一个引人注目的用例。 【参考方案1】:

正如 P1165R1 中所述,basic_stringoperator+ 的分配器传播规则很复杂,并且是不同库实现不一致的根源。

使有状态的分配器传播对于 operator+(basic_string) 更加一致

[...] basic_stringoperator+ 的分配器传播是随意的、不一致的,并且是实现分歧的根源。让他们保持一致。 [...]

P1165R1 已被 C++20 接受。

append() 成员函数与operator+(P1165R1 之前)没有相同的语义,没有重载,也没有遭受相同的“偶然性...” .前者没有理由加入后者的领域; basic_string 已经是一个容器的怪物(你的反例 optional 不是这种情况,它不是标准意义上的容器,即使它具有类似于 stl 容器的语义)。

【讨论】:

【参考方案2】:

您不能简单地添加&amp;&amp; 重载。您还必须创建原始函数&amp;。这可能是一个重大变化。

所有这些都是为了很少的收获。由于append() 修改了源字符串并返回*this 更多用于链接而不是将结果分配给其他任何东西,因此您编写的代码是单调的。如果需要,请使用+,这是为表达式结果构建字符串的惯用形式,并且已经具有必要的重载以提高效率:

auto s = "A"s + "B";

【讨论】:

我有两件事。这里是"A"s + "B",但是随着字符串变大并且有两个以上的字符串,这将在每个+ 上重新分配缓冲区,使其有点不理想。当&amp;&amp; 过载时,添加&amp; 将如何破坏代码?我不明白。 @koscelansky 检查overloads 6-12 是否有operator+ - 他们在内部进行移动。

以上是关于为啥 std::string append 在 rval ref 上没有重载?的主要内容,如果未能解决你的问题,请参考以下文章

c++ std::string::append函数

为啥'=='在std :: string上很慢?

为啥我可以在 std::map<std::string, int> 中使用 const char* 作为键

为啥这个程序会崩溃:在 DLL 之间传递 std::string

为啥 std::string_view 比 const char* 快?

为啥 std::string 引用不能采用 char*?