我们知道,C++98标准库都已经为C++11彻底翻修过,目的是为那些移动比复制更快的型别添加移动操作,但是如果你现有的代码没有为C++11做过专门修改,那性能也不会有很大提升,因为虽然说C++11原因为那些缺少移动操作的类生成移动操作,但有个前提条件,即该类未声明复制操作、移动操作以及析构函数;而且类中的数据成员或者父类禁用了移动,这也将导致编译期生成的移动操作被抑制掉。
另外,有些类的移动可能并不会比复制快多少,例如 std::array这个新容器类别,它实质上就是带有STL接口的内建数组,这一点和其他容器有很大区别,其他容器都是将其内容存放在堆上,然后维护一个堆指针,当需要移动时,只需要将指针重新赋值即可,但std::array的元素是直接存储在容器内,所以无论是复制还是移动,其复杂度都和std::array元素个数成正比,因为容器中的每个元素都必须逐一复制或移动,所以速度上可能不会有很大提升。
除了std::array外,还有一个我们常见类std::string,因为许多std::string都采用了小型字符串优化(SSO),采用了SSO后,小型字符串(一般是指容量不超过15个字符)会将内存存储在对象的某个缓冲区内,而非堆上,和std::array一样,当小型字符串发生移动或者复制时,需要将其元素逐一复制或移动,而非直接修改堆指针,所以它的移动也并不会比复制快多少。
总而言之,在以下几个场景下,C++11的移动语义并不会给你带来什么好处:
- 没有移动操作:待移动的对象没有提供移动操作,因此移动请求就变成了复制请求
- 移动未能更快:待移动的对象虽然有移动操作,但是并不一定比复制快
- 移动不可用:移动本可以发生的语境下,要求移动操作不可以发生异常,但是该操作未加上noexcept声明
- 源对象是个左值:除极少数特例外,只有右值可以作为移动操作的源