为什么会存在这种不对称性?
向后兼容,因为复制和移动之间的关系已经不对称。 MoveConstructible 的定义是 CopyConstructible 的一个特例,意味着所有 CopyConstructible 类型也是 MoveConstructible 类型。确实如此,因为采用对 const 的引用的复制构造函数将处理右值和左值。
可复制类型可以在没有移动构造函数的情况下从右值初始化(它可能不如使用移动构造函数的效率高)。
复制构造函数还可用于在移动基子对象时在派生类的隐式定义的移动构造函数中执行“移动”。
所以拷贝构造函数可以看成是“退化的移动构造函数”,所以如果一个类型有拷贝构造函数它并不严格需要移动构造函数,它已经是MoveConstructible,所以简单不声明移动构造函数是可以接受的。
反之则不然,活字不一定是可复制的,例如只移动类型。在这些情况下,删除复制构造函数和赋值提供了更好的诊断,而不是仅仅不声明它们并得到关于将左值绑定到右值引用的错误。
为什么不直接指定如果声明了移动操作,则不声明复制操作?
更好的诊断和更明确的语义。 “定义为已删除”是 C++11 中明确表示“不允许此操作”的方式,而不是只是碰巧被错误省略或因其他原因而遗漏。
移动构造函数和移动赋值运算符的“未声明”的特殊情况是不寻常的,并且由于上述不对称性而很特殊,但特殊情况通常最好保留在一些狭窄的情况下(这里值得注意的是“不声明”也可以应用于默认构造函数)。
另外值得注意的是,您提到的段落之一,[class.copy] p7,说(强调我的):
如果类定义没有显式声明复制构造函数,则隐式声明。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数定义为已删除;否则,它被定义为默认值(8.4)。 如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况。
“后一种情况”是指“否则定义为默认”部分。第 18 段对复制赋值运算符有类似的措辞。
所以委员会的意图是,在某些未来版本的 C++ 中,其他类型的特殊成员函数也会导致复制构造函数和复制赋值运算符被删除。原因是,如果您的类需要用户定义的析构函数,那么隐式定义的复制行为可能不会做正确的事情。出于向后兼容性的原因,尚未对 C++11 或 C++14 进行该更改,但其想法是,在将来的某个版本中,为了防止复制构造函数和复制赋值运算符被删除,您需要显式声明它们并将它们定义为默认值。
因此,如果复制构造函数可能没有做正确的事情,则删除它们是一般情况,而“未声明”是移动构造函数的特殊情况,因为复制构造函数无论如何都可以提供退化移动。