c++11中,显式声明拷贝构造、拷贝赋值或用户提供的析构函数(如~myclass(){})会抑制编译器隐式生成移动构造和移动赋值函数;因此仅删除拷贝函数不足以启用移动语义,必须显式声明myclass(myclass&&) = default;等移动成员。

想让类不可拷贝但可移动,光删拷贝函数远远不够——编译器不会因为你写了 = delete 就自动给你补上移动函数;相反,只要显式声明了任何拷贝操作或析构函数,移动函数就大概率被抑制生成。
为什么删了拷贝构造,std::move 还报错?
常见错误现象:error: use of deleted function 'MyClass::MyClass(MyClass&&)',哪怕你只写了 MyClass(const MyClass&) = delete;。
原因很直接:C++11/14 规则里,一旦你显式声明了拷贝构造、拷贝赋值、或用户提供的析构函数(哪怕只是 ~MyClass() {}),编译器就**不再隐式声明移动特殊成员函数**。它不会帮你生成 MyClass(MyClass&&),也不会生成 operator=(MyClass&&)。
所以不是“删完拷贝就自动可移动”,而是必须手动补全移动部分:
-
MyClass(MyClass&&) = default;或自定义实现 MyClass& operator=(MyClass&&) = default;- 避免写用户提供的析构函数(除非必要);若必须写,记得加
= default或明确支持移动后状态
拷贝和移动函数的声明必须配对且签名准确
容易踩的坑是签名不匹配,导致右值无法绑定:
- 拷贝构造必须是
MyClass(const MyClass&),删它就写MyClass(const MyClass&) = delete; - 移动构造必须是
MyClass(MyClass&&),不能加const(否则无法修改源对象) - 拷贝赋值:
MyClass& operator=(const MyClass&) = delete; - 移动赋值:
MyClass& operator=(MyClass&&) = default;
漏掉任意一个,比如只删拷贝构造但没删拷贝赋值,或者只定义了移动构造却忘了移动赋值,都会在赋值场景下触发编译错误。
资源类中移动后状态必须可控
用 = default 最省心,但前提是所有成员本身支持安全移动(如 std::unique_ptr、std::vector)。如果类里有裸指针、文件描述符或 socket 句柄,就得手动实现移动操作,并确保:
- 移动构造中,把
other.ptr的值转移过来后,立刻置为nullptr(或-1、INVALID_HANDLE_VALUE等) - 析构函数里检查资源是否为空,避免 double-free 或重复 close
- 移动赋值要处理自赋值(虽然右值很少自赋,但安全起见仍建议判等)
否则,移动后的对象若被二次析构或意外访问,行为未定义。
别让析构函数偷偷抑制移动函数生成
这是最容易被忽略的一点:C++11/14 中,只要类声明了用户提供的析构函数(哪怕只写 ~MyClass() {}),编译器就不会合成移动构造和移动赋值函数。
解决办法有两个:
- 完全不写析构函数,靠编译器默认生成(推荐,尤其当资源由 RAII 成员管理时)
- 如果必须写析构,就显式加上移动函数声明:
MyClass(MyClass&&) = default;和MyClass& operator=(MyClass&&) = default;,并确保析构逻辑能处理已移动对象(例如先判空再 delete)
别指望编译器“猜”你要移动语义——它只按规则办事,而规则就是:有用户声明的析构 → 不自动生成移动函数。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











