Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/158.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ std::string移动的实现_C++_String_Move Semantics - Fatal编程技术网

C++ std::string移动的实现

C++ std::string移动的实现,c++,string,move-semantics,C++,String,Move Semantics,我正在调查移动std::string的性能。在很长一段时间里,我一直认为字符串移动几乎是免费的,认为编译器将内联所有内容,并且只涉及一些廉价的赋值 事实上,我的移动思维模式是 string& operator=(string&& rhs) noexcept { swap(*this, rhs); return *this; } friend void swap(string& x, string& y) noexcept { //

我正在调查移动
std::string
的性能。在很长一段时间里,我一直认为字符串移动几乎是免费的,认为编译器将内联所有内容,并且只涉及一些廉价的赋值

事实上,我的移动思维模式是

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
更改为分配单个字段,则这是一种合法的实现

我非常惊讶地发现gcc实现了移动应用程序

这是一致的吗?同样重要的是,我不应该认为搬家几乎是免费的吗


令人困惑的是,达到了我的预期


虽然有一个可疑的
std::string::reserve

并不是一个确切的答案,但这是C++11
std::string
的新实现,没有引用计数器,并且具有小字符串优化,这导致了大量的组装。特别是,小字符串优化导致4个分支处理移动分配的源和目标的4种不同长度组合

当添加
-D_GLIBCXX_USE_cx11_ABI=0
选项时,将使用带有参考计数器的pre C++-11
std::string
,并进行不小的字符串优化


我不应该认为搬家几乎是免费的吗

《谈话》第47页写道:

复制和移动的比较

  • 许多人错误地认为搬家实际上是“免费的”
  • 复制和移动之间的性能差异很大
  • 对于基本类型(如int),复制或移动实际上是相同的
  • 当只需要传输对象的一部分来传输整个值时,移动比复制快

我只分析了GCC的版本。发生了这样的事情:代码处理不同类型的分配器。如果分配器具有
\u S\u move\u assign
\u S\u always\u equal
的特性,那么移动几乎是免费的,正如您所期望的那样。这是move
操作符=
中的
if

if (!__str._M_is_local()
    && (_Alloc_traits::_S_propagate_on_move_assign()
      || _Alloc_traits::_S_always_equal()))
          // cheap move
else assign(__str);
如果条件为true(
\u M\u is\u local()
表示小字符串,说明),则移动成本较低

如果为false,则调用normal
assign
(不是移动的)。当以下情况之一发生时,即为这种情况:

  • 字符串很小,因此
    assign
    将执行一个简单的memcpy(廉价)
  • 或者,分配器没有特征总是相等,也没有在移动分配时传播,因此分配将分配(不便宜)
这是什么意思

这意味着,如果您使用默认分配器(或具有前面提到的特征的任何分配器),那么移动仍然几乎是免费的


另一方面,生成的代码不必要地庞大,我认为可以改进。它应该有一个单独的代码来处理通常的分配器,或者有一个更好的
assign
code(问题是
assign
没有检查
\u M\u is\u local()
,但它会进行容量检查,因此编译器无法决定是否需要分配,因此它会不必要地将分配代码路径放入可执行文件中-您可以查看源代码中的确切细节)。

使用SSO(短字符串优化)移动可以复制,因为您无法移动数组。@NathanOliver我知道,但是
memcpy
已经涵盖了这一点。至少,我希望有一个大小为
sizeof(string)
的缓冲区来临时复制数组的内容,而不是创建一个临时stringAdd`-stdlib=libc++`到clang窗口。你仍然在libc++中发现了一个bug<代码>基本\u字符串::\u清除\u和\u收缩()应标记为
noexcept
。通过添加该选项,移动分配操作符可以很好地进行清理。
\uu clear\u和\u shrink
显然是最近的一次编辑。我在看主干libc++的顶端:
\u Alloc\u traits::\u S\u propagate\u on\u move\u assign
\u Alloc\u traits::\u S\u everys\u equal
constepr
函数。这些分支不应该在编译时被删除而不存在于程序集中吗?@MaximeGroushkin:它们不存在于程序集中。它首先在locality属性上分支(我在回答中包含的代码),因此
assign
代码必须在中编译。然后,
assign
代码不检查locality属性,而是检查容量(从
assign
的角度来看,这是可以理解的,因为容量在那里很重要,而不是位置)。编译器不够聪明(可以理解),无法将这两件事关联在一起,因此它将分配代码路径放入exe中。您可能需要提到
\u M_is_local
是用于小字符串优化的。