删除默认 C++ 复制和移动构造函数和赋值运算符的缺点?

Disadvantages to deleting default C++ copy and move constructors and assignment operators?

如果一个 class 的实例从不被复制也从不被移动,那么删除默认的移动和复制构造函数以及赋值运算符是否有任何缺点?这确实直接违反了 0 规则,但是否还有其他缺点(编译器会生成次优代码、次优数据布局等)?

没有缺点。但是也没有优点。它完全没有成就。编译器只会在需要时生成默认构造函数或运算符。如果不需要,正式删除与否都没有关系。

现在真正有所不同的是,当您明确想要强制执行 class 实例不能被复制、分配等时。有无数的原因导致某些特定的 class 可能不可赋值,或移动构造等。您始终可以定义适当的构造函数或运算符,并抛出异常。但是,在这种情况下,您会发现某些东西在运行时是错误的。

但是,您是否同意让编译器在编译时而不是运行时强制执行 no-move construction/assignment/whatever 会更好?因为如果把constructor/operator删了,哪里出错了,编译器马上就会骂你

特殊成员函数的存在与否对数据布局没有影响。好吧,至少在标准可以保证的程度上。也就是说,class 是否是标准布局(因此具有明确定义的布局)与其构造函数无关。它只与它的成员有关。如果 class 不是标准布局...那么标准不会保证任何内容。

然而,在较少的理论术语中,绝对没有理由没有特殊的构造函数来影响 class 的布局。

显然,标准无法强制执行 "optimal code" 生成。但是没有真正的理由说明为什么存在或不存在所述成员会导致为您定义的函数生成更糟糕的代码。

将一个特殊的成员函数声明为已删除除了在有人试图调用它时导致编译失败之外没有任何意义。

现在,有边缘情况,但这些都是围绕用户代码如何使用它们的。也就是说,如果您将对此类 class 的引用传递给一些打算移动它的模板代码。但它可以接受固定类型,但它必须使用较慢的算法来这样做(出于某种原因)。它将使用 std::is_move_constructible 来检测类型不可移动并使用较慢的算法。

This does fly directly against the rule of 0

...那又怎样?规则是使用默认语义,除非您的 class 需要其他语义。 unique_ptr 在概念上是一种复制没有意义的类型,因此它删除了复制操作。如果您的类型在概念上是一种复制和移动没有意义的类型,那么您应该删除这些操作。

请注意,"makes no sense" 表示它在逻辑上无效,具体取决于您的类型。根据定义,unique_ptr唯一拥有一个对象。复制指针意味着它并不唯一拥有该对象。由于这在逻辑上是无效的,因此无法复制 class。

如果您的 class 在概念上是不可移动的,那么您应该在 C++ 中使其不可移动。

If one has a class whose instances are never copied and never moved,

这可能意味着以下两种情况之一:

  1. 实例绝不能被复制或移动,因为这样做会导致不好的事情,例如浅拷贝,或者因为从语义的角度来看它没有意义.
  2. 目前没有实例被复制或移动。

are there any disadvantages to deleting the default move and copy constructors and assignment operators?

取决于这两种含义中哪一种是正确的:

  1. 没有。恰恰相反;你绝对应该删除它们以避免意外调用函数。
  2. 是的。 稍后当您的 class 的客户端代码更改并且需要复制或移动时,您将不得不撤消它。或者更糟的是,客户端程序员可能会实施不必要且容易出错的解决方法,包括指向实例的指针,或者通过首先从第一个实例中提取数据然后使用提取的数据构建新实例来手动复制(想象一下如果 std::stringstd::vector 不可复制)。