Files
CPlusPlusThings/site_extras/modern-cpp-cpp11-cpp-std-move.md
light-city dfe1c933b3 docs: address community issue feedback (#5 #338 #142 #246 #136 #303)
- const README: rewrite the space-saving claim per issue #5 follow-ups
  (constant-expression const vs #define immediates; which const qualify
  as constant expressions)
- const README: fix the class-section diagnosis per issue #338 (overload
  resolution picks the const overload; the restriction only surfaces
  once that overload is removed)
- sizeof README: virtual-inheritance result is compiler-dependent
  (GCC merges vbptr+vfptr -> 16, MSVC keeps them separate -> 32) #246
- static README: new section on internal linkage / access restriction
  with anonymous-namespace comparison #142
- 10-day practice: full problem list table + fixed day links #136
- new design_pattern/singleton/singleton_default.hpp: reusable
  thread-safe CRTP singleton template, compile+concurrency tested #303
2026-10-13 19:23:54 +08:00

76 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> 来自 [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 T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::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<Foo> p(new Foo());` 这条语句会产生编译错误。原因在于唯一所有权规则被应用。
但是,你可以使用 `std::move` 来转移指针的所有权:
```
data.push_back(std::move(p));
```
现在,指针的所有权被转移到了容器中,在容器生命周期结束时将被删除。虽然对象 `p` 仍然存在,但任何试图解引用它的尝试将生成空指针异常。
事实上,在移动操作之后,`p` 拥有的内部指针已经被设置为 `nullptr`。
综上所述,`std::move` 的作用在于提供了一种手段,能够安全地实现对象的移动语义,转变其左值属性为右值,使其可以被移动构造函数或移动赋值操作符所调用。正确地使用 `std::move` 可以提高性能并确保正确的所有权转移。
那么下一个问题,什么时候不需要使用std::move呢?