std::move配合insert或emplace_back可高效移动元素避免深拷贝,前提是类型支持移动语义;需注意移动后源对象处于有效但未定义状态,不可再访问其值,且对pod等平凡类型无收益。

用 std::move 配合 insert 或 emplace_back 实现高效移动
直接复制元素会触发拷贝构造,而移动语义能避免深拷贝开销。前提是元素类型支持移动(如 std::string、std::vector、自定义类含移动构造函数)。
常见错误是只调用 push_back 而没用 std::move,结果仍是拷贝:
dst.push_back(src[i]); // 拷贝!不是移动
正确写法:
-
dst.emplace_back(std::move(src[i]));—— 最常用,适用于逐个移动 -
dst.insert(dst.end(), std::make_move_iterator(src.begin()), std::make_move_iterator(src.end()));—— 一次性移动整个范围,但注意src之后处于有效但未定义状态 - 移动后记得清理源容器:若想清空
src,用src.clear();若只移走部分元素,需手动erase对应位置(见下节)
移动部分元素并从原 vector 中删除(即“剪切”)
如果目标是把 src 中某段(比如索引 [i, j))移到 dst 并从 src 删除,不能只移动再 clear —— 那会删掉全部。
关键点:先移动,再擦除。顺序反了会导致迭代器失效或越界:
- 用
std::make_move_iterator构造移动迭代器,传给dst.insert - 再用
src.erase(src.begin() + i, src.begin() + j)删除已移动的区间 - 注意:
erase返回新起始迭代器,但对索引操作更直观;若用迭代器遍历后 erase,必须处理返回值防止跳过元素
示例(移动前 3 个元素):
dst.insert(dst.end(), std::make_move_iterator(src.begin()), std::make_move_iterator(src.begin() + 3));<br>src.erase(src.begin(), src.begin() + 3);
移动后源 vector 元素的状态必须检查
移动不等于销毁,只是资源转移。被移动过的对象仍可析构、赋值、再次移动,但值是未定义的(标准规定为“有效但未指定”)。常见误用:
- 移动后还访问
src[i].size()或src[i].c_str()—— 行为未定义,可能 crash 或返回垃圾值 - 假设
std::vector<int></int>移动后变为空 —— 实际上int是平凡类型,std::move等价于拷贝,无意义;只有含动态资源的类型才真正受益于移动 - 对同一元素多次移动(如循环中反复
std::move(src[0]))—— 第二次移动的是已移出状态,结果不可靠
性能陷阱:不要在小对象或 POD 类型上滥用 std::move
对 int、double、std::array<int></int> 这类类型,std::move 不带来收益,反而干扰编译器优化。是否值得移动,看类型是否有非平凡移动构造函数:
-
std::is_trivially_move_constructible_v<t></t>为true→ 移动=拷贝,不用刻意写std::move -
std::string、std::vector<t></t>、std::unique_ptr→ 明确受益,该用就用 - 不确定时,用
sizeof(T)和构造/析构开销粗略判断:如果对象含指针+堆内存,大概率值得移动
真正省时间的地方,从来不是多敲几个字,而是避开不必要的堆分配和内存拷贝。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











