移动语义

1
2
3
4
5
6
7
// std::move 的实现
template <typename T>
typename remove_reference<T>::type &&move(T &&param) {
using ReturnType = typename remove_reference<T>::type &&;

return static_cast<ReturnType>(param);
}

通过 remove_reference 去掉 T 的引用性质(并不会去掉 cv 限定符),然后给它加上 &&,形成 ReturnType 类型,由于右值引用类型的返回值是右值,因此结果是实参被无条件地转换为右值。

如果没有定义移动构造函数,则使用拷贝构造函数。

std::move 是一个模板函数,用于将对象转换为右值引用。它并不直接对对象、值或内存进行任何实际的移动操作。std::move 的作用是告诉编译器可以“窃取”对象的资源,因为对象不再需要保持其原始状态。

  1. 对象转换:std::move 将一个左值(通常是一个命名对象)转换为右值引用。这种转换允许你调用对象的移动构造函数或移动赋值运算符。
  2. 资源转移:在使用 std::move 后,通常会调用对象的移动构造函数或移动赋值运算符,这些函数负责实际的资源转移(如指针、动态内存、文件句柄等)。
  3. 内存管理:std::move 本身不管理内存。内存的管理和资源的转移由移动构造函数或移动赋值运算符负责。
  4. std::move 通常用于将左值转换为右值引用,以便能够调用对象的移动构造函数或移动赋值运算符。然而,对于纯右值(即临时对象或字面值),std::move 通常是不必要并且多余的,因为它们本身已经是右值。

完美转发

保留参数的值类别(左值、右值),是否为 const 或 volatile 修饰符,并将其传递给其他函数。

std::forward 的实现

1
2
3
4
5
6
7
8
9
template< class T >
T&& forward(typename std::remove_reference<T>::type& t) noexcept {
return static_cast<T&&>(t);
}

template< class T >
T&& forward(typename std::remove_reference<T>::type&& t) noexcept {
return static_cast<T&&>(t);
}

怎么判断该用 move 还是 forward?

  • 对右值引用 move
    右值引用只能绑定到右值上,所以可以无条件地将它转换为右值
  • 对通用引用 forward
    通用引用既能绑定到左值上,也能绑定到右值上,在后一种情况下,我们希望能将它转换为右值
  • 在右值引用上调用 std::forward 表现出的行为是正确的,但由于 std::forward 没法自动做类型推导,写出来的代码会比较繁琐;但如果在通用引用上调用 std::move,可能会导致左值被错误地修改,导致异常的行为。

参考资料