一句话结论 :
std::move只是把左值无条件转换成右值引用(xvalue)的类型转换,本身不搬运任何资源;真正的移动发生在随后被选中的移动构造或移动赋值里。
标准库实现
标准库实现大致如下:
cpp
template<class T>
constexpr std::remove_reference_t<T>&& move(T&& t) noexcept {
return static_cast<std::remove_reference_t<T>&&>(t);
}
两个要点:
- 它只负责"改标签",让重载决议去匹配
T&&版本的函数。 - 对内置类型、可平凡复制的类型(
int、std::array<int,3>、std::complex等),移动等同拷贝,没有收益。
典型用法
cpp
// 1. 所有权转移
auto p = std::make_unique<Foo>();
auto q = std::move(p); // p 变为 nullptr
// 2. 避免深拷贝
std::vector<std::string> v;
std::string s(4096, 'x');
v.push_back(std::move(s)); // 搬指针,不复制 4096 个字符
// 3. 右值引用参数继续转交
void sink(std::string&& s) { store(std::move(s)); }
实现移动操作时,关键是让源对象进入"可安全析构"的状态:
cpp
class Buffer {
public:
Buffer(Buffer&& o) noexcept
: data_(std::exchange(o.data_, nullptr)),
size_(std::exchange(o.size_, 0)) {}
Buffer& operator=(Buffer&& o) noexcept {
if (this != &o) {
delete[] data_;
data_ = std::exchange(o.data_, nullptr);
size_ = std::exchange(o.size_, 0);
}
return *this;
}
// ...
};
需要注意的坑
- 对
const对象 move 等于拷贝。const string&&绑不到string&&,重载决议退回拷贝构造。 - 被移动后的对象处于"有效但未指定"状态。 只能析构或重新赋值;
unique_ptr保证为空,容器元素通常为空但不做保证。 return std::move(local);是负优化。 它阻止 NRVO。- 移动构造要标
noexcept。 否则vector扩容时move_if_noexcept会退化为拷贝,因为要维持强异常保证。 - 泛型转发用
std::forward,不要用std::move。 后者会无条件剥夺调用者对象,破坏左值实参。 - 移动裸指针不会置空源指针。 置空是
unique_ptr等智能指针的职责,不是std::move的。