std::move()详解

std::move 是将对象的状态或所有权从一个对象转移到另一个对象。它的源码在 /usr/include/c++/11/bits/move.h :


  /**
   *  @brief  Convert a value to an rvalue.
   *  @param  __t  A thing of arbitrary type.
   *  @return The parameter cast to an rvalue-reference to allow moving it.
  */
  template<typename _Tp>
    _GLIBCXX_NODISCARD
    constexpr typename std::remove_reference<_Tp>::type&&
    move(_Tp&& __t) noexcept
    { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

以上代码中的 std::remove_reference 源码位于 /usr/include/c++/11/type_traits


  // Reference transformations.

  /// remove_reference
  template<typename _Tp>
    struct remove_reference
    { typedef _Tp   type; };

  template<typename _Tp>
    struct remove_reference<_Tp&>
    { typedef _Tp   type; };

  template<typename _Tp>
    struct remove_reference<_Tp&&>
    { typedef _Tp   type; };

从源码可以看出,std::move 并不移动任何东西,它只是一个强制类型转换,将一个左值引用强制转换为右值引用,以便于使一个右值引用能够指向该左值, 简单地说,move 相当于一个类型转换:static_cast<T&&>(lvalue)。

内存拷贝和内存搬运是一个即耗费时间又耗费空间的工作,c++ 11 中引入 std::move 以及 右值引用 都是为了避免拷贝内存或搬运内存以加快运行速度,提高程序效率。

std::move 将左值转换为右值从而避免了内存拷贝,本文开头所说的将对象的状态或所有权从一个对象转移到另一个对象,也只有转移没有内存拷贝,内存搬运。

需要强调的是,被 move 后的对象的状态是不确定的,在 C++ 标准中有如下描述:

17.6.5.15 Moved-from state of library types [lib.types.movedfrom]
1 Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may
be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be
placed in a valid but unspecified state.
§ 17.6.5.15
ISO/IEC 14882:2011(E)
©

上面一段话的意思是,在 std::move 之后,源对象仍然是一个有效的 C++ 对象,但他的状态是未定义的。我们应该避免使用他。下面的代码可以测试一下这个未定义的行为:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main(int argc, char* argv[])
{
    int a = 5;
    int && rref = move(a);
    cout << "a = " << a << endl;
    cout << "rref = " << rref << endl;
    cout << "&a = " << &a << endl;
    cout << "&rref = " << &rref << endl;
    
    string foo = "foo-string";
    string bar = "bar-string";
    vector<string> myvector;

    myvector.push_back (foo);               // copies
    myvector.push_back (move(bar));         // moves

    cout << "myvector contains:";
    for (string& x:myvector) cout << ' ' << x;
    cout << '\n';

    cout << "foo = " << foo << endl;
    cout << "bar = " << bar << endl;
    cout << "size of bar = " << bar.size() << endl;
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *