关于std :: string移动的实现

时间:2018-05-29 14:24:38

标签: c++ string move-semantics

我正在研究移动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}}

2 个答案:

答案 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代码(问题是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,复制或移动实际上是相同的
  •   
  • 当只需要传输部分对象以传输整个值时,移动比复制更快
  •