本文内容概览
在使用C++的过程中,我们可能都接触过移动构造函数(Move constructor),在使用移动构造函数的过程中,有一个很重要的函数 std::move()
,本文将详细介绍这一内容:
std::move()
是什么
从技术上来看, std::move()
是一个函数,但或许它并不是一个真正的函数。它更像是一个转换器,在编译器考虑表达式值的方式之间进行转换。
std::move()
做了什么
首先要注意的是,std::move()
实际上并没有移动任何东西。它将表达式从左值(lvalue)(例如命名变量)转换为x值(xvalue)。x值告诉编译器:
你可以掠夺、移动我持有的任何东西并在别处使用(反正我很快就会被销毁)。1
换句话说,当你使用 std::move(x)
时,本质上是允许编译器对 x
拥有的资源进行利用。因此,如果 x
有自己的内存缓冲区,那么在 std::move(x)
之后,这块资源将被 x
释放,编译器可以将其分配给另一个对象。
std::move()
什么时候用
这一问题等价于“什么时候我要利用现有对象的资源?”,在构造函数、运算符方法等地方经常需要这样做,因为在这些地方对象会自动频繁地创建和销毁。当然,这只是一个经验法则。
一个典型的用例是将资源从一个对象“移动”到另一个对象,而不是复制。下面给出一个简单直接的示例:以更少的复制交换两个对象。
如果使用拷贝构造函数:
template <class T>
swap(T& a, T& b) {
T tmp(a); // 我们创建了a的第二份副本
a = b; // 我们创建了b的第二份副本(并丢弃了a的副本)
b = tmp; // 我们创建了tmp的第二份副本(并丢弃了b的副本)
}
而当我们使用 std::move
方法,我们就可以做到交换资源而不是到处复制它们:
template <class T>
swap(T& a, T& b) {
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}
设想,当T
是一个大小为 n 的 vector<int>
时会发生什么。在第一个版本中,你需要读取和写入 3 * n 个元素,而在第二个版本中,你基本上只需要读取和写入指向 vector 缓冲区的 3 个指针,加上 3 个缓冲区的大小。当然,类 T 需要知道如何进行移动操作;为了使其工作,你的类需要有一个移动赋值运算符和一个移动构造函数。
参考资料
[1]: https://stackoverflow.com/questions/3413470/what-is-stdmove-and-when-should-it-be-used
[2]: https://web.stanford.edu/class/cs106l/lectures/13_Move_Semantics_S24.pdf
https://en.cppreference.com/w/cpp/language/value_category ↩︎