移动构造函数仅在参数为右值且存在可访问的ClassName(ClassName&&)重载时被编译器优先选择,它依赖值类别和重载解析规则,非自动优化开关;临时对象、std::move转换左值、容器push_back右值参数等场景可触发,但C++17起返回局部对象常被强制省略构造。

移动构造函数只在参数是右值、且存在可访问的 ClassName(ClassName&&) 重载时,才会被优先选择。 它不是“自动启用”的优化开关,而是编译器根据值类别(value category)和重载解析规则严格选出的结果。下面分几种典型情况说明。
传入临时对象时自动触发移动
临时对象(如函数返回的局部对象、字面量构造的匿名对象)天然属于右值,只要类定义了移动构造函数,编译器就会选它而非拷贝构造:
-
MyClass a = MyClass();—— C++17 起 guaranteed copy elision 直接绕过构造;C++11/14 中若未被 RVO 优化,则调用移动构造 -
std::vector<int> v1 = std::vector<int>{1,2,3};—— 右值初始化,触发std::vector的移动构造 -
func(MyClass());(func参数为MyClass值类型)—— 临时对象传值,先移动构造形参
注意:如果类没定义移动构造,即使传的是临时对象,也会退回到拷贝构造(前提是拷贝构造可用)。
std::move 显式转换左值后才可能触发移动
std::move 本身不做任何移动操作,只是把左值强制转成 T&& 类型,让重载解析“有机会”选中移动构造函数:
立即学习“C++免费学习笔记(深入)”;
-
MyClass a; MyClass b(std::move(a));—— 若MyClass有MyClass(MyClass&&),则调用移动;否则调用拷贝 -
const MyClass x; MyClass y(std::move(x));——std::move(x)得到的是const MyClass&&,无法绑定非常量右值引用,最终匹配拷贝构造(或报错,若拷贝构造被删) -
MyClass a; std::move(a);单独写这一行毫无效果:没目标接收这个右值引用,不会触发任何构造
函数返回局部对象时是否调用移动取决于优化
函数返回局部对象是常见误判点:
-
MyClass create() { MyClass tmp; return tmp; }→auto x = create(); - C++17 起:强制省略(guaranteed copy elision),不调用任何构造函数,
tmp直接在x的存储位置构造 - C++11/14:若编译器未启用 RVO,会尝试调用移动构造;若移动构造被删或不可访问,则回退到拷贝构造(若可用)
- 所以不能依赖“函数返回一定触发移动”,得看编译器行为 + 是否禁用了 RVO(如
-fno-elide-constructors)
容器操作中 push_back / emplace_back 的区别
往 std::vector 等容器插入对象时,移动是否发生,取决于你用什么接口和传什么参数:
-
v.push_back(MyClass());—— 临时对象是右值,触发移动构造(若可用) -
v.push_back(std::move(x));—— 显式移动左值x,同样走移动构造 -
v.emplace_back(...);—— 不构造临时对象,直接在容器内就地构造,完全绕过移动/拷贝 -
v.push_back(x);——x是左值,调用拷贝构造(除非你重载了push_back(MyClass&&))
真正容易被忽略的是:移动后源对象进入“有效但未指定状态”,比如 std::vector 移动后内部指针变 nullptr,但你不该再读它的 size() 或 data() —— 标准只保证能安全析构或赋值,不保证还能用。


















