> 来自 [C++那些事网站](https://light-city.github.io/stories_things/modern-cpp/cpp11/cpp-std-move/) 的文章,由站点同步脚本导出。 # C++ std::move 原理与用法 `std::move` 是一个非常重要的函数,它提供了一种方式,可以将一个左值对象标记为一个特殊类型的右值对象,即 xvalue。这种转变是为了允许执行移动语义,而不是执行常规的拷贝语义。 - 为什么要使用 std::move - 实际应用场景 ## 1.为什么要使用 std::move 通常情况下,只有右值对象才能进行移动操作,因为移动构造函数使用右值引用作为参数,而右值引用只能绑定到右值。因此,如果你想从一个由左值表达式表示的对象中执行移动操作,你需要使用 `std::move` 来将其转换为一个 xvalue(特殊的右值)。 使用 `std::move` 时需要谨慎,因为移动操作会将对象置于一个通常只允许销毁的状态。因此,最好仅对对象的最后一次使用进行移动操作。 `std::move` 并不实际执行移动,需要明确的是,`std::move` 并不会实际执行对象的移动操作。它只是将给定的左值对象转换为右值引用。下面是 `std::move` 的一个可能实现: ```cpp template typename remove_reference::type&& move(T&& t) { return static_cast::type&&>(t); } ``` ## 2.实际使用场景 ### 2.1 移动构造而非拷贝构造 在函数 `f1` 中,参数 `a` 是一个右值引用的左值,而不是右值本身,因为它有一个名称。因此,为了强制执行 `Foo` 的移动构造,我们必须将其转换为一个右值,而 `std::move` 恰好可以完美地做到这一点。 ```cpp struct Foo { ... Foo(const Foo&) {} Foo(Foo&&) {} }; void f1(Foo&& a) { Foo b(a); // 调用拷贝构造函数,而非移动构造函数 } void f2(Foo&& a) { Foo b(std::move(a)); // right } ``` ### 2.2 所有权管理 假设有如下代码: ``` Foo* p = new Foo(); data.push_back(p); ``` 上述代码可以正常工作,但你无法确定指针的所有权归属。谁来删除指针?或者指针应该在何时被删除?它会在容器被销毁时被删除吗? 在 C++11 中,`std::unique_ptr p(new Foo());` 这条语句会产生编译错误。原因在于唯一所有权规则被应用。 但是,你可以使用 `std::move` 来转移指针的所有权: ``` data.push_back(std::move(p)); ``` 现在,指针的所有权被转移到了容器中,在容器生命周期结束时将被删除。虽然对象 `p` 仍然存在,但任何试图解引用它的尝试将生成空指针异常。 事实上,在移动操作之后,`p` 拥有的内部指针已经被设置为 `nullptr`。 综上所述,`std::move` 的作用在于提供了一种手段,能够安全地实现对象的移动语义,转变其左值属性为右值,使其可以被移动构造函数或移动赋值操作符所调用。正确地使用 `std::move` 可以提高性能并确保正确的所有权转移。 那么下一个问题,什么时候不需要使用std::move呢?