Why does the copy assignment operator return a reference / const reference?

In C ++, the concept of returning a link from a copy assignment operator is not clear to me. Why can't the copy statement return a copy of a new object? Also, if I have class A and the following:

A a1(param); A a2 = a1; A a3; a3 = a2; //<--- this is the problematic line 

The operator = is defined as follows:

 AA::operator =(const A& a) { if (this == &a) { return *this; } param = a.param; return *this; } 
+47
c ++ assignment-operator copy-constructor operator-overloading
Jun 23 '10 at 21:45
source share
6 answers

Strictly speaking, the result of the copy assignment operator should not return a link, although to simulate the default behavior used by the C ++ compiler, it should return a non-constant link to the object to which it is assigned (the implicitly generated copy assignment operator will return a non-constant link - C ++ 03: 12.8 / 10). I saw a fair bit of code that returns void from the copy destination overload, and I cannot remember when this caused a serious problem. Returning void prevent users from using the destination chain ( a = b = c; ) and will prevent using the destination result in a test expression, for example. Although this type of code is in no way audible, I also do not consider it particularly common - especially for non-primitive types (if the interface for the class is not intended for such tests, for example for iostreams).

I do not recommend you to do this, simply indicating that it is allowed, and that this does not seem to cause a lot of problems.

These other SO questions are related (maybe not completely fooled) that have information / opinions that might interest you.

  • Has anyone found a need to declare the return parameter of an assignment operator of a copy of const?
  • Overloading an assignment operator in C ++
+52
Jun 23 '10 at 23:31
source share

A little clarification as to why it is preferable to return by reference for operator = compared to returning by value --- since the chain a = b = c will work fine if the value is returned.

If you return the link, minimal work will be done. The values โ€‹โ€‹of one object are copied to another object.

However, if you return by value for operator = , you will call the AND destructor constructor. After some time, the assignment operator is called !!

So, given:

 A& operator=(constA& rhs){ ... }; 

Then

 a=b=c;// calls assignment operator above twice. Nice and simple. 

but

 A operator=(constA& rhs){ ... }; a=b=c; // calls assignment operator twice, calls copy constructor twice, calls destructor type to delete the temporary values! Very wasteful and nothing gained! 

In general, there is nothing that could be returned at a cost, but much to lose.

(Note. This is not intended to address the benefits of having an assignment operator return l. Read other posts on why this might be preferable)

+42
Jan 23 2018-11-11T00:
source share

When you overload operator= , you can write it to return the type you want. If you want strong enough, you can overload X::operator= to return (for example) an instance of some completely different class Y or Z This is usually extremely impractical.

In particular, you usually want to support the operator= chain, like C. For example:

 int x, y, z; x = y = z = 0; 

In this case, you usually want to return the lvalue or rvalue of the assigned type. This only leaves the question of whether to return a reference to X, a reference constant to X or X (by value).

Returning a reference to constant X is usually a bad idea. In particular, a const reference can be bound to a temporary object. The lifetime of a temporary object extends to the lifetime of the link to which it is bound, but not recursively, to the lifetime of what can be assigned. This makes it easier to return a dangling reference - a const reference refers to a temporary object. This lifetime of the object extends to the lifetime of the link (which ends at the end of the function). By the time the function returns, the lifetime of the reference and temporary ends, so the assigned link is a dangling link.

Of course, returning a non-constant link does not provide complete protection against this, but at least makes you work a little harder. You can (for example) define some local ones and return a link to it (but most compilers can and will warn about it too).

Returning a value instead of a link has both theoretical and practical problems. On the theoretical side, you have a basic disconnect between = , which usually means what it means in this case. In particular, when an assignment usually means "take this existing source and assign its value to this existing destination", it begins to mean something more than "take this existing source, create a copy of it and assign this value to this existing destination."

From a practical point of view, especially before rvalue references were invented, this could have a significant impact on performance - creating an entire new object when copying AB was unexpected and often quite slow. If, for example, I had a small vector and assigned it to a larger vector, I would expect at most to copy the elements of a small vector plus a (small) fixed invoice to adjust the size of the destination vector. If two copies are involved instead: one from the source to the tempo, the other from temp to the destination, and (even worse) the dynamic allocation for the time vector, my expectation of the complexity of the operation will be completely destroyed. For a small vector, the time for dynamic distribution can be many times higher than the time for copying elements.

The only other option (added in C ++ 11) is to return the rvalue link. This can easily lead to unexpected results - chain assignment of type a=b=c; can destroy the contents of b and / or c , which would be rather unexpected.

This returns a returning normal link (and not a constant reference or a rvalue reference) as the only possibility that (reasonably) ensures that most people want it to work reliably.

+8
Nov 18 '14 at 2:04
source share

Partly because returning a reference to self is faster than returning by value, but in addition, it allows you to use the original semantics in primitive types.

+4
Jun 23 '10 at 21:52
source share

operator= can be defined to return what you want. You should be more specific as to what the problem really is; I suspect you have a copy constructor using operator= internally, and this causes a stack overflow, since the copy constructor calls operator= , which should use the copy constructor to return A by value to infinity.

+4
Jun 23 2018-10-23T00:
source share

There is no language requirement in the resulting user operator= type, but the standard library has this requirement:

C ++ 98 ยง23.1 / 3:

"The type of objects stored in these components must comply with CopyConstructible types (20.1.3) and additional requirements for Assignable types.

C ++ 98 ยง23.1 / 4:

" In table 64, T is the type used to instantiate the container, T is the value of T , and u is the value (possibly const ) of T

enter image description here




Returning a copy by value will still support the chain of the chain, for example a = b = c = 42; , because the assignment operator is right-associative, i.e. it is parsed as a = (b = (c = 42)); . But returning a copy would forbid meaningless constructs like (a = b) = 666; . For a small class returning a copy, it would probably be the most efficient, while for a larger class returning by reference, it would usually be the most efficient (and the copy is unacceptably inefficient).

Until I found out about the standard library requirement, I used operator= return void , for efficiency and to avoid the absurdity of supporting bad code based on side effects.




With C ++ 11, the result type T& for default is additionally required - for the assignment operator, since

C ++ 11 ยง8.4.2 / 1:

" . A function that is explicitly prohibited by default must have the [& hellip;] same type of function declared (with the possible exception of different ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be "reference to non-const T ", where T is the name of the class of member functions), as if it were declared implicitly

+3
Nov 18. '14 at 2:14
source share



All Articles