42

From CPP Reference:

Deleted implicitly-declared copy assignment operator The implicitly-declared or defaulted copy assignment operator for class T is defined as deleted in any of the following is true:

T has a non-static data member that is const
T has a non-static data member of a reference type.
T has a non-static data member that cannot be copy-assigned (has deleted, inaccessible, or ambiguous copy assignment operator)
T has direct or virtual base class that cannot be copy-assigned (has deleted, inaccessible, or ambiguous move assignment operator)
T has a user-declared move constructor
T has a user-declared move assignment operator 

So that tells me what causes the deletion but not the why? Can anyone explain for:

T has a non-static data member of a reference type.

and whether this will suffice in my class to deal with the deleted operator:

T& T:operator=(T& t){};

if I have a member of a base class which is a reference type.

Do I need to do anything in my operator= such as explicitly declare return *this or will the compiler (g++) handle this for me? Do I have to do anything special with the reference member? Sorry for noob question but I am new to C++ having started off with managed languages (C# and Java).

didierc
  • 14,572
  • 3
  • 32
  • 52
Luthervd
  • 1,388
  • 2
  • 14
  • 25

2 Answers2

37

References are bound to an object when they are initialized and can never be altered after that, everything else you do to them affects the object they are bound to, not the reference itself.

So a reference member is set during construction, and never altered. Since the purpose of an assignment operator is to alter members after construction, it doesn't make sense to generate an implicit assignment operator when one of the member can never be altered. The compiler refuses to try and guess what you want it to do and forces you to provide your own assignment operator with the semantics you want.

Do I need to do anything in my operator= such as explicitly declare return *this or will the compiler (g++) handle this for me?

You absolutely definitely 100% need to return *this;

The only time you don't need an explicit return in C++ is if your function returns void or in main() (where there is an implicit return 0; if you reach the end of the function) or in unusual cases such as functions that never return (either looping forever or throwing an exception).

Do I have to do anything special with the reference member?

It depends what semantics you expect assignment of your type to have.

If you don't want it to change the object the reference is bound to, fine, do nothing with it.

If you want assignment to alter the object the reference is bound to, you need to do that.

If you want the reference to be re-bound to a different object, you're out of luck, C++ doesn't allow that.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Good answer - selected this one as you also covered off return *this. I'm enjoying c++ but can see how much easier it is to get yourself in a muddle. – Luthervd Nov 15 '14 at 13:37
  • I find reasoning in this answer confusing: copy assignment operator of a reference variable does not alter the reference but the object that the reference is bound too, so why is that not an assumed default implementation of copy assignment for reference members? What do I misunderstand or miss? – trybik Jul 07 '22 at 09:01
  • @trybik it could give surprising semantics. Assignment usually alters the object itself, without spooky action at a distance. To avoid surprises, C++ doesn't allow an implicitly-defined assignment operator to write through reference members. That's just how C++ works. If you want assignment to write through a reference, you can still do that, you just have to define the assignment operator yourself. – Jonathan Wakely Jul 08 '22 at 09:44
  • @Jonathan Wakely many thanks for a quick reply - appreciated; spooky action at distance is what reference variables do, but not of what object members do, so you say it's a design choice - fair enough, but I am fishing for some less arbitrary and more in-depth explanation (what if example for surprising semantics / code being unintuitive), or even some general design guideline backing it up as "explicit is better than implicit" (preferably with some follow-up read references); IMVHO this would improve your answer vastly in contrast to a very confusing argument that starts now the 2nd paragraph – trybik Jul 09 '22 at 16:52
10

A reference cannot be changed. The object referenced by the reference can be changed, but the reference itself cannot.

Consider

struct A {
  int &r;
  A(int &r_) : r(r_) { }
};

int main() {
  int i, j;
  A a1(i), a2(j);
  a1 = a2;
}

There is nothing you could possibly do in any custom operator= implementation that would make a1.r refer to j, and that's why no operator= gets created or should be created on your behalf.

If you have a situation in which it's okay for operator= not to change that reference, then you can define your own.

If you have a situation where you need operator= to change a reference, your class shouldn't be using a reference. A pointer might be more appropriate.

  • 3
    The poor compiler. It had nothing to do with this question, yet it gets backpinged all the time for things it had no say in. – Kerrek SB Nov 15 '14 at 13:25
  • @KerrekSB Heh. I could have said "and that's why the standard says the compiler shouldn't create an `operator=` on your behalf, and except as noted otherwise in the documentation, the compiler follows the standard", but I think that's needlessly verbose. :) –  Nov 15 '14 at 13:27
  • Exactly. You could have stopped at "that's what the standard says". – Kerrek SB Nov 15 '14 at 14:09
  • @KerrekSB I've re-worded to not mention the compiler. I don't think your suggestion is good enough: if the standard says no `operator=` should be created, and an implementor feels the standard is just being silly, an implementor can provide an `operator=` as an extension, so long as it makes sure this doesn't mess up SFINAE. It's not solely on the standard. –  Nov 15 '14 at 14:39
  • 2
    A copy-assignment overload of `operator=` is *always* declared, either implicitly or explicitly. It may or may not end up being defined as deleted. – Kerrek SB Nov 15 '14 at 14:45
  • @KerrekSB Oh, wait, perhaps I misunderstood. If you hinting at the fact that SFINAE wouldn't work, then good point. That has the unexpected bonus that an implementation could far more easily provide it as an extension. –  Nov 15 '14 at 14:50