为什么 如果类的成员是引用,则复制对象是非法的

Why It is illegal to copy an object if a member of the class is a reference?

本文关键字:复制 对象 非法 引用 如果 成员 为什么      更新时间:2023-10-16

我遇到了一个测验,说下面第 18 行的代码格式不正确,因为"当需要复制的成员之一是引用时,使用隐式定义的赋值运算符是格式不正确的。

我无法理解。为什么无法复制参考文献?为什么16号线是合法的?第 16 行与第 18 行非常相似,复制构造函数仍然需要执行复制,对吧?

1 #include <iostream>
2
3 struct A
4 {
5   A(int& var) : r(var) {}
6
7   int &r;
8 };
9
10 int main(int argc, char** argv)
11 {
12   int x = 23;
13
14   A a1(x);
15
16   A a2 = a1;
17
18   a2 = a1;
19
20   return 0;
21 }

16 行使用复制构造函数,第 18 行使用赋值运算符operator=。两种不同的功能,具有不同的限制。

由于引用无法重新绑定,因此编译器无法生成有意义的隐式赋值运算符。因此,它拒绝这样做,并生成错误。

复制构造函数

是第一次生成对象,因此它可以像在自己的构造函数中一样绑定该引用。

具有

引用成员的类没有默认提供的复制/移动赋值运算符。建立绑定后,无法重新绑定引用以引用其他变量。简而言之,复制构造函数正在进行初始建立,而默认赋值运算符将尝试在绑定后更改它。

因此,该标准对默认复制和移动分配运算符都调用了这种情况。

C++11 § 12.8p23

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

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

您当然可以编写自己的重载。

#include <iostream>
struct A
{
    A(int& var) : r(var) {}
    int &r;
    A& operator=(const A& obj)
    {
        r = obj.r; // value copied, reference-binding remains the same
        return *this;
    }
};
int main(int argc, char** argv)
{
    int x = 42;
    int y = 43;
    A a1(x);
    A a2(y);
    A a3 = a1; // legal. default copy-ctor invoked
    a3 = a2;   // legal. user-defined copy-assignment invoked
    std::cout << x << ',' << y << 'n';
    return 0;
}

输出

43,43

但这不会(也不能)重新绑定引用。此处提供的重载会更改引用的数据;不是参考文献本身。这种区别很重要。

希望这有帮助。

因为重新

分配给引用在C++是非法的。

int &a = some_int;
a = some_other_int; // value copied not reference
a = some_int; // value copied not reference

当您使用赋值运算符(由编译器生成)时,它会盲目地复制对象,从而尝试重新分配给您的引用,因此无效。

当你说a2 = a1;时,编译器会尝试将a1.r重新分配给a2.r使其在编译时失败,因为它是错误的格式。

您可以将引用视为automatically dereferenced constant pointer。因此,行a2 = a1;将保持格式错误,原因与下面的类相同。

struct A
{
  A(int *var) : p(var) {}
  int * const p;
};