我正在研究移动string& operator=(string&& rhs) noexcept
{
swap(*this, rhs);
return *this;
}
friend void swap(string& x, string& y) noexcept
{
// for disposition only
unsigned char buf[sizeof(string)];
memcpy(buf, &x, sizeof(string));
memcpy(&x, &y, sizeof(string));
memcpy(&y, buf, sizeof(string));
}
的表现。在最长的时间里,我认为字符串移动几乎是免费的,认为编译器会内联一切,只会涉及一些廉价的任务。
事实上,我的移动心理模型实际上是
memcpy
据我所知,如果将std::string::reserve
更改为指定单个字段,则此法律实施。
我非常惊讶地发现gcc的移动涉及creating a new string and might possibly throw due to the allocations despite being noexcept
的实现。
这甚至符合要求吗?同样重要的是,我不应该认为移动几乎是免费的吗?
令人尴尬,std::vector<char>
compiles down对我所期待的事情。
clang's implementation有很大的不同,尽管存在可疑的{{1}}
答案 0 :(得分:1)
我只分析了GCC的版本。这里发生了什么:代码处理不同类型的分配器。如果分配器具有_S_propagate_on_move_assign
或_S_always_equal
的特征,那么移动几乎是免费的,正如您所期望的那样。这是移动if
中的operator=
:
if (!__str._M_is_local()
&& (_Alloc_traits::_S_propagate_on_move_assign()
|| _Alloc_traits::_S_always_equal()))
// cheap move
else assign(__str);
如果条件为真(_M_is_local()
表示小字符串,描述here),则移动便宜。
如果为false,则调用 normal assign
(不是移动的)。在以下任何一种情况下都是这种情况:
assign
会做一个简单的memcpy(便宜)这是什么意思?
这意味着,如果您使用默认分配器(或任何具有前面提到的特征的分配器),那么移动仍然几乎是免费的。
另一方面,生成的代码不必要地巨大,我认为可以改进。它应该有一个单独的代码来处理通常的分配器,或者有一个更好的assign
代码(问题是assign
没有检查_M_is_local()
,但它会进行容量检查,因此编译器无法决定是否需要分配,因此它会将分配代码路径放入可执行文件中 - 您可以查看源代码中的确切详细信息。)
答案 1 :(得分:0)
不完全是答案,但这是没有引用计数器的C ++ 11 std::string
的新实现,以及导致大量汇编的小字符串优化。特别是,小字符串优化会导致4个分支处理移动分配的源和目标长度的4种不同组合。
添加-D_GLIBCXX_USE_CXX11_ABI=0
选项以使用预C ++ - 11 std::string
时带有引用计数器且没有小字符串优化the assembly code looks much better。
我不应该认为移动几乎是免费的吗?
在Nothing is Better than Copy or Move by Roger Orr发言,slides第47页,它说:
复制和移动的比较
- 许多人错误地认为移动有效'自由'
- 复制和移动之间的性能差异差异很大
- 对于原始类型,例如int,复制或移动实际上是相同的
- 当只需要传输部分对象以传输整个值时,移动比复制更快