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

时间:2015-01-06 05:30:13

标签: c++ copy copy-constructor

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

我无法理解。为什么参考无法复制?为什么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 }

3 个答案:

答案 0 :(得分:6)

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

因为引用无法反弹,所以编译器无法生成任何有意义的隐式赋值运算符。因此它拒绝这样做,并产生错误。

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

答案 1 :(得分:3)

具有引用成员的类没有默认提供的复制/移动赋值运算符。一旦建立绑定,引用就不能反弹以引用不同的变量。简而言之,复制构造函数正在进行初始化,而默认赋值运算符将尝试在绑定后更改

因此,该标准将此情况称为默认复制和移动赋值运算符。

  

C ++11§12.8p23

     

如果X具有:

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

你当然可以写你自己的重载。

#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

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

希望这有帮助。

答案 2 :(得分:1)

因为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;
};