std::move仅是将左值转为右值引用的类型转换,不执行移动操作;真正移动由已定义的移动构造/赋值函数完成,移动后对象处于有效但未指定状态,仅可析构或赋值,不可访问内容。

std::move 不会自动提速,它只在你明确放弃原对象所有权、且目标类型已正确定义移动语义时才生效;用错地方反而拖慢性能或引发未定义行为。
std::move 后原对象还能用吗
不能假设可用。标准只要求移动后对象处于“有效但未指定状态”——可析构、可赋值,但不可读取内容。
-
v2 = std::move(v1)之后,v1.size()可能返回 0(如std::vector),也可能不为 0(取决于实现),但继续用v1[0]或v1.data()是未定义行为 - 自定义类若没在移动构造函数里把裸指针置为
nullptr,后续析构时二次delete会崩溃 - 调试时可在移动后加断言:
assert(v1.empty());—— 仅对标准容器有意义,自定义类需自己定义“空”状态
哪些类型用 std::move 真正有效
只对管理动态资源的类型才有意义:堆内存、文件句柄、锁、网络连接等。内置类型和小结构体完全没必要。
- ✅ 有效:
std::vector、std::string、std::unique_ptr、std::thread - ❌ 无效:
int、double、std::array<int></int>、std::pair<int int></int>—— 编译器本就会按值传递,std::move反而干扰优化 - ⚠️ 半吊子:
std::shared_ptr移动只是转移控制块指针,不省内存,但避免原子计数器增减,微弱收益
什么时候必须显式写 std::move
仅当你持有左值,但调用接口只接受右值引用时才需要。编译器不会自动帮你转。
- 向容器插入左值:
v.push_back(std::move(s));—— 因为push_back有T&&重载 - 调用只接收右值的函数:
process(std::move(ptr));,其中process声明为void process(std::unique_ptr<t>&&)</t> - 在移动赋值运算符中转发成员:
data_ = std::move(other.data_); - ❌ 不要写:
return std::move(local_obj);—— 阻断 RVO,得不偿失 - ❌ 不要写:
func(std::move(x));在模板转发函数里 —— 应该用std::forward<t>(x)</t>
const 对象能不能 std::move
不能。const 左值无法绑定到非 const 的右值引用参数,编译直接失败。
-
const std::string s = "hello"; auto x = std::move(s);→ 编译错误:无法将const std::string&&绑定到std::string&& - 即使你绕过(比如加
const_cast),标准库移动构造函数通常声明为MyClass(MyClass&&),不接受 const 右值引用 - 结论:对 const 对象,
std::move没意义,也不合法
最易被忽略的是:std::move 的效果完全依赖目标类型的移动构造函数是否真正“掏空”了原对象。没写或写错(比如忘了置空指针),std::move 就只是个无害但无用的类型转换。性能提升不在 std::move 这一行,而在那个被调用的、正确实现的移动构造函数里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











