如果一个类有引用数据成员,为什么编译器不会合成默认的赋值运算符?

5
在C++中,如果一个类有一个引用数据成员,则编译器不会自动生成默认的赋值运算符。为什么呢?

该引用将被初始化为什么? - Mat
9
你不能重新分配一个引用。仅仅进行分配会修改被引用的对象,这可能不是你想要的。 - Peter Wood
2个回答

6
在C++中,如果一个类有一个引用数据成员,则编译器不会合成默认赋值运算符。为什么?
《C++03标准》12.8/13中定义了复制赋值应该做什么:
每个子对象都以适当的方式分配: - 如果子对象是类类型,则使用该类的复制赋值运算符(就像通过显式限定符一样;即忽略更派生类中可能存在的任何虚拟重载函数); - 如果子对象是数组,则对每个元素按照元素类型的方式进行赋值; - 如果子对象是标量类型,则使用内置的赋值运算符。
简而言之,这意味着每个成员都应以适当的方式分配,这就引出了问题:在类的赋值中,引用成员应该如何处理?考虑以下关于引用的内容: 1. 引用本质上是不可分配的,它们保持指向初始化时的同一引用对象[Ref 1]。 2. 由于#1,将引用赋值给另一个引用并不会重新分配引用,而是改变了所引用对象的值,这是非直观的行为。
这里没有默认的正确行为需要强制执行,而是一个情境性的行为。因此,C++标准规定,如果一个类具有引用数据成员,则该类的设计者处于最佳位置来确定此行为,因此决定如果一个类具有引用数据成员,则编译器不会合成默认赋值运算符。
这个决定在C++03标准12.8/12中指定:
当其类类型的对象被分配其类类型的值或从其类类型派生的类类型的值时,隐式声明的复制赋值运算符在其类类型的对象被分配其类类型的值或从其类类型派生的类类型的值时隐式定义。如果隐式定义复制赋值运算符的类具有: …… - 非静态数据成员是引用类型,或者 …… 则程序是不良形式的。
[Ref 1] 《C++03标准》8.5.3/2中指出: 引用在初始化后不能更改其引用的对象。请注意,引用的初始化与对其的赋值处理方式非常不同。参数传递(5.2.2)和函数返回值(6.6.3)都是初始化操作。

4

在一个仅限会员的论坛上看到了关于此问题的讨论。由于大多数程序员并不熟悉答案,因此我想在这里发布答案并分享。

根据C++标准草案N3337 §12.8.23:

如果类X具有以下特征,则默认的复制/移动赋值运算符被定义为删除:

  • 具有非平凡对应赋值运算符的变体成员,并且X是类似联合体的类;或者
  • 常量非类类型(或其数组)的非静态数据成员;或者
  • 引用类型的非静态数据成员;或者
  • 类类型M(或其数组)的非静态数据成员,由于重载分辨率(13.3),应用于M的相应赋值运算符导致歧义或函数被删除或从默认赋值运算符中无法访问;或者
  • 不能被复制/移动的直接或虚拟基类B,因为重载分辨率(13.3),作为B的相应赋值运算符,导致歧义或函数被删除或从默认赋值运算符中无法访问;或者
  • 对于移动赋值运算符,具有没有移动赋值运算符并且不是平凡可复制的类型的非静态数据成员或直接基类,或任何直接或间接虚拟基类。

1
很高兴你引用了标准中的一句话,但更重要的是理解其中的原因:赋值运算符必须给LHS对象的引用成员分配一个新值,但引用不能被重新分配(它们隐式地是const)。 - HighCommander4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接