【问题标题】:Why do reference type members cause implicitly-declared copy assignment operator to be deleted为什么引用类型成员会导致隐式声明的复制赋值运算符被删除
【发布时间】:2015-01-12 19:13:53
【问题描述】:

来自CPP Reference:

已删除隐式声明的复制赋值运算符 类 T 的隐式声明或默认复制赋值运算符被定义为已删除,以下任何一项为真:

T has a non-static data member that is const
T has a non-static data member of a reference type.
T has a non-static data member that cannot be copy-assigned (has deleted, inaccessible, or ambiguous copy assignment operator)
T has direct or virtual base class that cannot be copy-assigned (has deleted, inaccessible, or ambiguous move assignment operator)
T has a user-declared move constructor
T has a user-declared move assignment operator 

所以这告诉我是什么导致了删除,而不是为什么?谁能解释一下:

T has a non-static data member of a reference type.

这是否足以在我的课堂上处理已删除的运算符:

T& T:operator=(T& t){};

如果我有一个作为引用类型的基类的成员。

我是否需要在我的operator= 中做任何事情,例如显式声明 return *this 或者编译器 (g++) 会为我处理这个吗?我必须对参考成员做任何特别的事情吗?很抱歉这个菜鸟问题,但我是 C++ 新手,刚开始使用托管语言(C# 和 Java)。

【问题讨论】:

  • 如何复制一个隐含删除了operator= 的对象? memcpy()?
  • 例如:memcpy(&destinationObject, &sourceObject, sizeof(sourceObject));

标签: c++


【解决方案1】:

无法更改引用。引用所引用的对象可以改变,但引用本身不能。

考虑

struct A {
  int &r;
  A(int &r_) : r(r_) { }
};

int main() {
  int i, j;
  A a1(i), a2(j);
  a1 = a2;
}

没有您可以在任何自定义 operator= 实现中使 a1.r 引用 j,这就是为什么不创建或不应该创建 operator= 的原因代表你。

如果您的情况是 operator= 可以不更改该引用,那么您可以定义自己的引用。

如果您遇到需要 operator= 更改引用的情况,您的班级不应该使用引用。指针可能更合适。

【讨论】:

  • 可怜的编译器。它与这个问题无关,但它一直在为它没有发言权的事情提供支持。
  • @KerrekSB 呵呵。我本可以说“这就是为什么标准说编译器不应该代表您创建operator=,除非文档中另有说明,否则编译器遵循标准”,但我认为这是不必要的冗长。 :)
  • 没错。你本可以停留在“这就是标准所说的”。
  • @KerrekSB 我重新措辞不提编译器。我认为您的建议不够好:如果标准说不应该创建 operator=,并且实施者认为标准只是愚蠢,实施者可以提供 operator= 作为扩展,只要它确保这不会弄乱 SFINAE。它不仅符合标准。
  • operator= 的复制分配重载总是声明,无论是隐式还是显式。它可能最终被定义为删除,也可能不会。
【解决方案2】:

引用在初始化时就绑定到一个对象,之后就永远无法更改,您对它们所做的任何其他事情都会影响它们所绑定的对象,而不是引用本身。

所以引用成员是在构造过程中设置的,并且永远不会改变。由于赋值运算符的目的是在构造后更改成员,因此当成员之一永远无法更改时,生成隐式赋值运算符是没有意义的。编译器拒绝尝试猜测您想要它做什么,并强制您为自己的赋值运算符提供您想要的语义。

我是否需要在我的 operator= 中做任何事情,例如显式声明 return *this 或编译器 (g++) 是否会为我处理这个?

你绝对 100% 需要return *this;

在 C++ 中唯一不需要显式返回的情况是,如果您的函数返回 voidmain()(如果您到达函数末尾,则存在隐式 return 0;)或异常诸如永远不会返回的函数(永远循环或抛出异常)的情况。

我需要对引用成员做任何特别的事情吗?

这取决于您希望类型分配具有什么语义。

如果您不希望它更改引用绑定到的对象,很好,什么也不做。

如果您希望分配更改引用绑定到的对象,则需要这样做。

如果您希望将引用重新绑定到不同的对象,那么您很不走运,C++ 不允许这样做。

【讨论】:

  • 很好的答案 - 选择了这个,因为您还涵盖了 return *this。我很喜欢 c++,但可以看到让自己陷入混乱是多么容易。
猜你喜欢
  • 2019-04-12
  • 2020-04-13
  • 2013-10-16
  • 1970-01-01
  • 1970-01-01
  • 2019-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多