显式声明拷贝构造函数会阻止移动构造函数自动生成;析构函数(包括虚析构)的用户声明同样静默禁用移动操作;移动操作之一被声明则另一个不会隐式生成;成员类型不支持移动也会传导性抑制外层类的隐式移动构造。

显式声明拷贝构造函数会阻止移动构造生成
只要类里写了 MyClass(const MyClass&),哪怕只是一行空实现,编译器就**不会自动生成移动构造函数**。这不是“优先级”问题,而是标准明确禁止:一旦用户介入了拷贝语义的控制,编译器就假定你对资源管理有特殊要求,不再擅自添加移动语义。
常见误判场景:
- 只写了拷贝构造但没写移动构造,又期望
std::vector<myclass></myclass>插入临时对象时高效移动——实际触发的是拷贝,可能慢一个数量级 - 把拷贝构造声明为
explicit或加了额外参数(如MyClass(const MyClass&, bool)),仍算“显式声明”,移动构造照样被抑制
析构函数是隐式移动的“高危开关”
哪怕只写了一个空的 ~MyClass() {},移动构造和移动赋值都会被静默禁用。这点比拷贝构造更隐蔽,因为析构函数看起来“无害”,但它向编译器传递了“这个类需要自定义清理逻辑”的信号,从而关闭所有自动生成的移动操作。
特别注意:
立即学习“C++免费学习笔记(深入)”;
- 虚析构函数(
virtual ~MyClass())同样触发该规则 - 继承链中只要任意基类有用户声明的析构函数,派生类的隐式移动构造也会失效
- 用
= default显式恢复析构函数(~MyClass() = default;)可绕过此限制,但必须确保它出现在类定义中、且没有其他冲突声明
移动操作本身的存在会覆盖隐式生成
只要声明了 MyClass(MyClass&&) 或 operator=(MyClass&&) 中的任意一个,编译器就认为“移动语义已由用户接管”,不会再为你补全另一个(即使你只写了移动构造,没写移动赋值)。
这导致两个典型陷阱:
- 只实现移动构造但漏掉移动赋值,容器在扩容重排时可能退化为拷贝赋值
- 把移动构造写成
noexcept(false),而标准库容器(如std::vector::reserve)在需要强异常安全时会拒绝调用它,转而使用拷贝构造
成员类型不支持移动也会传导性禁用
即使类自身完全满足隐式生成条件,只要某个非静态数据成员的类型**没有可用的移动构造函数**(例如含 const 成员、引用成员,或其移动构造被 = delete),整个类的隐式移动构造就会被抑制。
检查方法很简单:
- 对成员类型调用
std::is_move_constructible_v<t></t>,返回false就说明它拖累了外层类 -
std::string、std::vector等标准类型没问题;但自定义类若删了移动构造或含const int x;,就会卡住外层
真正容易被忽略的是:这种禁用是静默的,编译器既不报错也不警告,只有运行时性能下降或 static_assert(std::is_move_constructible_v<myclass>)</myclass> 失败时才暴露出来。


















