如果我想使类不可复制,"operator="返回类型是否重要?

Does "operator=" return type matter if I want to make the class non-copyable?

本文关键字:operator 返回类型 是否 可复制 如果      更新时间:2023-10-16

>假设我有一个不支持成员复制的类,所以我不想保留编译器实现的复制构造函数和赋值运算符。我也不想实现这些,因为

  1. 这样做需要额外的努力,我不需要在我的课堂上或
  2. 这些操作在我的课堂上没有意义

所以我想禁止他们。为此,我将声明它们是私有的,并且不提供任何实现:

class NonCopyable {
private:
   NonCopyable( const NonCopyable& ); //not implemented anywhere
   void operator=( const NonCopyable& ); //not implemented anywhere
};

现在我可以为成员函数选择任何返回类型operator=()。我选择哪种返回类型有关系吗?

不,返回类型无关紧要。

C++ 标准不会对您自己声明的复制赋值特殊成员函数的返回类型施加任何要求。它只需要是一个operator=(),接受"恰好一个类型为XX&const X&volatile X&const volatile X&的参数"。††因此,void operator=( const NonCopyable& );仍然是复制赋值运算符(具体来说,是用户声明的运算符(。

由于您实际上提供了自己的复制赋值运算符,因此它将抑制默认复制赋值运算符的生成。这会强制对 NonCopyable 的复制赋值运算符的所有调用解析为您的,从而导致任何使用复制赋值运算符的尝试都无法编译,因为它被声明为 private

class Foo : NonCopyable
{
};
int main()
{
    Foo a;
    Foo b;
    // Compiler complains about `operator=(const NonCopyable&)`
    // not accessible or something like that.
    a = b;
}

而且由于我永远无法实际使用它,因此它不完全是规范的复制赋值运算符并不重要。如果我尝试使用复制赋值运算符,则会导致编译器错误,这正是您想要的。


† 当然,从风格上讲,复制赋值运算符是否真的做了某事,这确实很重要。通常,您希望操作员的行为与内置运算符一样,因此在实际执行分配时,返回X&是一种很好的做法。

†† C++ 标准:12.8 复制类对象 [class.copy]

9 用户声明的复制赋值运算符X::operator=是类X的非静态非模板成员函数,只有一个 类型 XX&const X&volatile X&const volatile X& 的参数。

不,因为您永远不会在代码中调用此运算符。我倾向于保留返回类型NonCopyable&,以保持清晰和一致性。

这很重要:

  • void确保来自类实现中的一小部分意外/误导调用(a = b = c/f(d = e((产生编译时错误而不是链接时错误,这可以节省编译时间并且更容易理解(与许多开发人员接触的大型类的相关性最小,其中一些开发人员先前熟悉程度有限(。

  • void会为我(希望是大多数开发人员(敲响警钟,想知道您是否:

    • 想要删除默认生成的operator=
    • 只是懒得额外的打字,或者
    • 熟悉/漠不关心operator=普遍预期的语义。

考虑到悬而未决的问题,其他程序员不太可能出现并认为您只是没有提供实现并随便添加它(您可能觉得注释就足够了(。

  • 返回对类型的引用可能会使整个函数签名更易于即时识别,或者直观地搜索复杂类型以查找可能产生相反效果operator= - 所有这些都在旁观者的眼睛(和头脑(中。

不,因为你可以从 operator= 返回任何内容,即使你定义了它的实现。

不,没关系,因为你从不实现return语句。如果尝试调用运算符,编译器将无法找到实现(具有任何return类型,因此返回类型无关紧要(。

有趣的是,boost::noncopyable 的复制赋值运算符被声明为返回一个const noncopyable&,但我想这只是约定。