为啥 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
您可以看到,后一个示例将产生更少的分配,因此在这种情况下,最好移动一些看起来像临时的东西。我的问题是为什么他们(委员会)不根据上下文在&&
上添加简单的重载以使append
的结果为std::string&
或std::string&&
?我的意思是类似于std::optional
的事情正在处理value
。有没有例子可以证明这种优化是假的?
【问题讨论】:
你能发布一个实际有用的例子吗?自动 res = std::string("A").append("B");剂量真的很有意义。 @Cortex 是的,这没有意义,但是当您尝试连接几个字符串时,可能会非常大,您希望尽可能少地重新分配。而append
将重用缓冲区,因此总体分配会更少。
解决方法:std::string s = std::move(std::string("A").append("B"));
但对所有这一切都很重要。这不是一个引人注目的用例。
【参考方案1】:
正如 P1165R1 中所述,basic_string
的 operator+
的分配器传播规则很复杂,并且是不同库实现不一致的根源。
使有状态的分配器传播对于 operator+(basic_string) 更加一致
[...]
basic_string
的operator+
的分配器传播是随意的、不一致的,并且是实现分歧的根源。让他们保持一致。 [...]
P1165R1 已被 C++20 接受。
append()
成员函数与operator+
(P1165R1 之前)没有相同的语义,没有重载,也没有遭受相同的“偶然性...” .前者没有理由加入后者的领域; basic_string
已经是一个容器的怪物(你的反例 optional
不是这种情况,它不是标准意义上的容器,即使它具有类似于 stl 容器的语义)。
【讨论】:
【参考方案2】:您不能简单地添加&&
重载。您还必须创建原始函数&
。这可能是一个重大变化。
所有这些都是为了很少的收获。由于append()
修改了源字符串并返回*this
更多用于链接而不是将结果分配给其他任何东西,因此您编写的代码是单调的。如果需要,请使用+
,这是为表达式结果构建字符串的惯用形式,并且已经具有必要的重载以提高效率:
auto s = "A"s + "B";
【讨论】:
我有两件事。这里是"A"s + "B"
,但是随着字符串变大并且有两个以上的字符串,这将在每个+
上重新分配缓冲区,使其有点不理想。当&&
过载时,添加&
将如何破坏代码?我不明白。
@koscelansky 检查overloads 6-12 是否有operator+
- 他们在内部进行移动。以上是关于为啥 std::string append 在 rval ref 上没有重载?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我可以在 std::map<std::string, int> 中使用 const char* 作为键
为啥这个程序会崩溃:在 DLL 之间传递 std::string