在 C++ 中,如果类具有引用数据成员,则编译器不会合成默认赋值运算符。为什么?
2 回答
在 C++ 中,如果类具有引用数据成员,则编译器不会合成默认赋值运算符。为什么?
复制分配应该做什么定义在:
C++03 标准 12.8/13:
每个子对象都以适合其类型的方式分配:
如果子对象是类类型,则使用该类的复制赋值运算符(好像通过显式限定;也就是说,忽略更多派生类中任何可能的虚拟覆盖函数);
如果子对象是数组,则以适合元素类型的方式分配每个元素;
如果子对象是标量类型,则使用内置赋值运算符。
简而言之,这意味着每个成员都应该以适当的方式分配,
这引发了一个问题,
在课堂上分配参考成员的行为应该是什么?
考虑以下关于引用的内容:
- 引用本质上是不可分配的,它们一直引用它们被初始化的同一个引用[Ref 1]。
- 由于
#1
分配给引用不会重新分配引用,它会更改引用的值,这是非直观行为。
这里没有要强制执行的默认正确行为,而是一种相当情境的行为。因此,C++ 标准要求类的设计者处于确定这种行为的最佳位置,因此决定默认赋值运算符不应该由编译器合成,如果类有一个引用数据成员。
此决定在:
C++03 标准 12.8/12 中指定:
当为其类类型的对象分配其类类型的值或从其类类型派生的类类型的值时,隐式声明的复制赋值运算符被隐式定义。如果隐式定义了复制赋值运算符的类具有:
.......
— 引用类型的非静态数据成员,或
.......
[参考 1]
C++03 标准 8.5.3/2:
初始化后不能将引用更改为引用另一个对象。请注意,引用的初始化与分配给它的处理方式非常不同。参数传递(5.2.2)和函数值返回(6.6.3)是初始化。
在仅限会员的论坛上看到了讨论。由于大多数程序员都不知道答案,所以想在这里发布答案并分享。
来自 C++ 标准草案 N3337 §12.8.23:
如果 X 具有以下条件,则将类 X 的默认复制/移动赋值运算符定义为已删除:
- 具有非平凡对应赋值运算符且 X 是类联合类的变体成员,或
- const 非类类型(或其数组)的非静态数据成员,或
- 引用类型的非静态数据成员,或
- 无法复制/移动的类类型 M(或其数组)的非静态数据成员,因为重载决议 (13.3) 应用于 M 的相应赋值运算符会导致歧义或函数从默认赋值运算符,或
- 无法复制/移动的直接或虚拟基类 B,因为应用于 B 的相应赋值运算符的重载解析 (13.3) 会导致歧义或从默认赋值运算符中删除或无法访问的函数,或
- 对于移动赋值运算符,非静态数据成员或直接基类,其类型不具有移动赋值运算符且不可平凡复制,或任何直接或间接虚拟基类。