mirror of
https://github.com/Light-City/CPlusPlusThings.git
synced 2026-10-28 19:04:45 +08:00
- 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
This commit is contained in:
76
site_extras/modern-cpp-cpp11-cpp-std-move.md
Normal file
76
site_extras/modern-cpp-cpp11-cpp-std-move.md
Normal file
@@ -0,0 +1,76 @@
|
||||
> 来自 [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呢?
|
||||
Reference in New Issue
Block a user