5

I met a quiz saying that the code in line 18 below is ill-formed because "It is ill-formed to use an implicitly defined assignment operator when one of the members that will need to be copied is a reference. "

I couldn't understand that. Why reference could not be copied? Why Line 16 is legal? Line 16 is quite similar to line 18, a copy constructor still need to do the copy, right?

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 }
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
athos
  • 6,120
  • 5
  • 51
  • 95

3 Answers3

6

Line 16 uses a copy constructor, line 18 uses the assignment operator operator=. Two different functions with different restrictions.

Because a reference can't be rebound, the compiler can't generate an implicit assignment operator that makes any sense. Thus it refuses to do so, and generates an error.

A copy constructor is generating the object for the first time, so it can bind that reference the same way you did in your own constructor.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
3

A class with a reference member has no default-provided copy/move assignment operators. References cannot be rebound to reference a different variable once binding is established. In short, the copy constructor is making that initial establishment, while the default assignment operator would be trying to change it after-binding.

The standard therefore calls this case out for both default copy and move assignment operators.

C++11 § 12.8p23

A defaulted copy/move assignment operator for class X is defined as deleted if X has:

  • a variant member with a non-trivial corresponding assignment operator and X is a union-like class, or
  • a non-static data member of const non-class type (or array thereof), or
  • a non-static data member of reference type, or
  • a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the defaulted assignment operator, or
  • a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the defaulted assignment operator, or
  • for the move assignment operator, a non-static data member or direct base class with a type that does not have a move assignment operator and is not trivially copyable, or any direct or indirect virtual base class.

You can certainly write your own overload.

#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;
}

Output

43,43

But this will not (and cannot) rebind the reference. The overload provided here changes the referenced data; not the references themselves. Such a distinction is important.

Hope this helps.

WhozCraig
  • 65,258
  • 11
  • 75
  • 141
1

Because it is illegal in C++ to reassign to a reference.

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

When you use assignation operator (generated by compiler), it blindly does the copy of objects and thus try to reassign to your reference and hence is invalid.

When you say a2 = a1;, compiler would try to reassign a1.r to a2.r making it fail at compile time because it is ill-formation.

You can think of a reference as an automatically dereferenced constant pointer. So the line a2 = a1; will remain ill-formatted for the same reason as for the class below.

struct A
{
  A(int *var) : p(var) {}
  int * const p;
};
Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
  • 3
    It's illegal to rebind a reference. There isn't even syntax for it. The statement you labeled as illegal, however, is not. – Benjamin Lindley Jan 06 '15 at 05:37
  • You dropped the erroneous comment, but your reasoning is still incorrect, because `a1.r = a2.r` would not be ill-formed. Nor would an initialization of one reference from another be ill-formed, as it would simply bind the second reference to the first reference's referent. – Benjamin Lindley Jan 06 '15 at 05:46
  • i tried `int some_int = 3; int some_other_int = 5; int &a = some_int; some_int = 4; a = some_other_int; some_other_int = 6;` it works – athos Jan 06 '15 at 06:32
  • 2
    @athos define "works". When your commented snippet completes, `some_int` will be `5`, `some_other_int` will be `6`, and `a` *still* references `some_int` (which is as already mentioned: `5`). – WhozCraig Jan 06 '15 at 07:09