首先,我想说 Mats Petersson 的答案比公认的更好,因为它提到了理由。
其次,作为补充,我想详细说明一下。
隐式声明(或默认)移动 ctor 的行为
来自c++draft:
非联合类 X 的隐式定义的复制/移动构造函数执行其基类和成员的成员复制/移动。
编译器隐式声明移动 ctor 时的条件
从cppreference:
如果没有为类提供用户定义的移动构造函数,则以下所有情况均成立:
- 没有用户声明的复制构造函数
- 没有用户声明的复制赋值运算符
- 没有用户声明的移动赋值运算符
- 没有用户声明的析构函数
然后编译器将声明一个移动构造函数作为其类的非显式内联公共成员,并带有签名T::T(T&&)
。
为什么 dtor(和许多其他人)会阻止隐式声明的移动 ctor?
如果我们看上面的条件,不仅用户声明的析构函数可以防止隐式声明的移动 ctor,用户声明的复制构造函数、用户声明的复制赋值运算符和用户声明的移动赋值运算符都具有相同的预防效果。
正如 Mats Petersson 所指出的,其基本原理是:
如果编译器认为您可能需要在移动操作中执行成员移动以外的操作,那么假设您不需要它是不安全的。
当有用户声明的析构函数时,这意味着需要进行一些清理工作,那么您可能希望对移出的对象进行处理。
当有用户声明的移动赋值运算符时,因为它也是“移动”资源,你可能希望在移动 ctor 中做同样的事情。
当存在用户声明的复制构造函数或复制赋值运算符时,这是最有趣的情况。我们知道,移动语义允许我们在获得性能优化的同时保持值语义,并且当未提供移动 ctor 时,移动将“回退”到复制。在某种程度上,移动可以被视为“优化副本”。因此,如果复制操作需要我们做一些事情,很可能在移动操作中也需要做类似的工作。
由于在上述条件下,可能需要执行成员移动以外的操作,编译器不会假定您不需要它,因此不会隐式声明移动 ctor。