在 C++ 中,std::move
是一个标准库函数,位于
头文件中,用于将一个对象显式地转换为右值引用。这并不会真正地“移动”对象,而是允许“移动语义”发生,即触发资源的转移而非拷贝,提高效率。
template<typename T>
typename remove_reference<T>::type&& move(T&& t) noexcept;
T&&
转换为 T&&
(右值引用),使得可以使用移动构造或移动赋值。#include
#include
#include // std::move
int main() {
std::string src = "Hello, World!";
std::string dest = std::move(src); // 移动构造
std::cout << "dest: " << dest << std::endl;
std::cout << "src (moved-from): " << src << std::endl; // 不再保证有效内容
}
输出:
dest: Hello, World!
src (moved-from):
std::string generateMessage() {
std::string msg = "This is a long message...";
return std::move(msg); // 显式 move,避免拷贝(但现代编译器 RVO 优化可能自动处理)
}
注意:对于返回局部变量时,C++17 起编译器会自动优化(RVO),
std::move
可能反而阻止优化,需谨慎使用。
emplace_back
)#include
#include
int main() {
std::vector<std::string> vec;
std::string temp = "temporary string";
vec.push_back(std::move(temp)); // 触发移动构造
}
class MyClass {
std::string data;
public:
MyClass(std::string&& str) : data(std::move(str)) {}
};
它只是类型转换工具,最终是否执行移动取决于对象类型是否支持 移动构造函数 / 移动赋值运算符。
操作 | 描述 | 性能 | 典型使用 |
---|---|---|---|
拷贝构造 T(const T&) |
拷贝资源 | 慢 | 多用于值语义 |
移动构造 T(T&&) |
资源转移 | 快 | 优化临时对象传递 |
std::move(t) |
类型转换为右值引用 | 快 | 搭配移动构造使用 |
std::move
时的注意事项std::move
的对象之后不能再被正常使用std::string s = "hello";
std::string t = std::move(s);
// s 的资源已被转移到 t,s 可能是空的!
std::cout << s << "\n"; // 合法但未定义内容,可能是空字符串
建议:std::move
后的对象只能被销毁或赋新值,不要继续依赖其旧的值。
std::move
只是类型转换,不等于移动了资源void foo(std::string&& s) {
std::cout << s << std::endl;
}
std::string str = "abc";
foo(std::move(str)); // str 被转换为右值引用,但资源是否移动取决于 foo 内部是否 move(s)
只有函数内部调用 std::move(s)
才真正触发移动构造或移动赋值。
const std::string s = "hello";
std::string t = std::move(s); // 实际是拷贝构造!因为移动构造不能修改 const
原因:移动构造需要修改源对象(例如置空原指针),而 const
禁止修改。
错误示例:
std::vector<int> getVec() {
std::vector<int> v = {1,2,3};
return std::move(v); // 不需要 move,编译器自动优化为移动(RVO)
}
说明:
return std::move(v)
反而可能阻止返回值优化(RVO)。return v;
,让编译器决定。emplace_back
,不一定需要 std::movestd::vector<std::string> v;
v.push_back(std::move(s)); // 需要 std::move
v.emplace_back("hello"); // 自动构造,无需 std::move
std::string getName() {
return std::string("abc");
}
std::string name = std::move(getName()); // 多余!getName() 是右值,自动移动
std::move
使用 checklist场景 | 是否使用 std::move |
---|---|
将局部变量返回 | ❌ 不推荐,用 return 优化 |
传入函数参数 | ✅ 用于 T&& 参数调用 |
插入容器时使用已存在对象 | ✅ vec.push_back(std::move(obj)); |
对 const 对象 | ❌ 无效,触发的是拷贝 |
对临时对象 | ❌ 多余,自动是右值 |
使用后是否还会用该对象 | ❌ 不要 move,还要用它的话 |
下面以为一个完整的 PointCloud
类实现,包括:
Point3D
PointCloud
Point3D
与 PointCloud
// point_cloud.h
#pragma once
#include
#include
#include // for std::move
struct Point3D {
float x, y, z;
Point3D() : x(0), y(0), z(0) {}
Point3D(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}
};
class PointCloud {
public:
PointCloud() = default;
// 移动构造函数
PointCloud(PointCloud&& other) noexcept {
points_ = std::move(other.points_);
}
// 移动赋值运算符
PointCloud& operator=(PointCloud&& other) noexcept {
if (this != &other) {
points_ = std::move(other.points_);
}
return *this;
}
// 禁用拷贝(可选)
PointCloud(const PointCloud&) = delete;
PointCloud& operator=(const PointCloud&) = delete;
void addPoint(const Point3D& pt) {
points_.emplace_back(pt);
}
void print() const {
for (const auto& pt : points_) {
std::cout << "(" << pt.x << ", " << pt.y << ", " << pt.z << ")\n";
}
}
std::size_t size() const { return points_.size(); }
private:
std::vector<Point3D> points_;
};
// main.cpp
#include "point_cloud.h"
int main() {
PointCloud cloud;
cloud.addPoint({1.0f, 2.0f, 3.0f});
cloud.addPoint({4.0f, 5.0f, 6.0f});
cloud.addPoint({7.0f, 8.0f, 9.0f});
std::cout << "Original PointCloud:\n";
cloud.print();
// 使用移动构造函数
PointCloud movedCloud = std::move(cloud);
std::cout << "\nMoved PointCloud:\n";
movedCloud.print();
std::cout << "\nOriginal PointCloud after move (should be empty):\n";
cloud.print();
return 0;
}
输出示例:
Original PointCloud:
(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
Moved PointCloud:
(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
Original PointCloud after move (should be empty):
const std::string s = "hello";
std::string t = std::move(s); // 拷贝发生,而不是移动!
std::move(obj)
将 obj
变成右值引用,支持移动构造或赋值。std::forward
搭配使用时,常出现在模板函数中(完美转发)。