Must parameter of assignment operator be reference? - c++

When overloading assignment operator of a class in C++, must its parameter be reference?
For example,
class MyClass {
public:
...
MyClass & operator=(const MyClass &rhs);
...
}
Can it be
class MyClass {
public:
...
MyClass & operator=(const MyClass rhs);
...
}
?
Thanks!

The parameter of an overloaded assignment operator can be any type and it can be passed by reference or by value (well, if the type is not copy constructible, then it can't be passed by value, obviously).
So, for example, you could have an assignment operator that takes an int as a parameter:
MyClass& operator=(int);
The copy assignment operator is a special case of an assignment operator. It is any assignment operator that takes the same type as the class, either by value or by reference (the reference may be const- or volatile-qualified).
If you do not explicitly implement some form of the copy assignment operator, then one is implicitly declared and implemented by the compiler.

Generally, it's up to you to decide, there is no must. The first variant is common and "canonic" and is ok for any assignment operator implementation.
When the question is speed, I think you should read this article about passing-by-value technique. This means that in some cases passing by value would be more effective than passing by const reference.
Also to mention, your second variant doesn't need const keyword, because passing by value acts as if a copy was created, so the original object definitely won't be changed.

C++ Operator Overloading Guidelines suggest, that the assignment operator gets a const reference. According to the site, the reason is that we do not want to change the argument (since const), but just the left hand side of the operator. Thus it saves time to pass it by reference.
It also points to the reason, why also a reference is returned by the assignment operator - operator chaining. In order to get a = (b = 1) working, it's necessary that (b = 1) returns a reference that can be assigned (=) to a.

Do you know the copy and swap idiom for exception safe assignment?
MyClass& operator=(const MyClass& rhs)
{
MyClass copy(rhs);
swap(copy);
return *this;
}
The implementation can be simplified (and in some cases sped up) via call by value:
MyClass& operator=(MyClass copy)
{
swap(copy);
return *this;
}

Ok I had this problem and I couldn't find a good answer so I'm going to share what I learned.
You could pass by value there is nothing wrong with that. (as you showed in your question.)
But the reason we pass the parameter by const reference is so the function doesn't make an actual copy of the value being called in. Its referenced, so its just pointing at that value wherever it is in the memory.
This saves processing time especially if its something big that has thousands of names...
In this case the time saved would be almost nothing.
And for the const, that ensures the user of the function that the referenced value is not going to be changed because it could be changed since you have access to the original location in the memory because its passed by reference..
If your function definition actually changes the value of the parameter being called in by const reference , it will be a compiler error, it wont let you do that. because when you put const, you are telling the compiler this value cannot be changed.

Related

Using of Operator Overloading form

I wonder in c++ about the operator overloading forms of the below like:
ClassName & operator+(ClassName &other)
ClassName operator+(ClassName &other)
Classname operator+(const ClassName &other)
Classname operator+(const Classname other)
Classname operator+(Classname other)
What are the difference of usage ??
Just for reference: none of the quoted operators is the one normally
actually overloaded! The one which is normally overloaded is more like
this (assuming it is in the same namespace as ClassName):
ClassName operator+ (ClassName const& op1, ClassName const& op2) {
ClassName rc(op1);
rc += op2;
return rc;
}
There are some variations how this operator can be implemented. In
particular the first argument may be passed by value to avoid the copy
in the implementation: in some cases the copy could be elided. This particular implementation using a named variable and returning it by name is intended to enable copy elision (as #juanchopanza pointed out, return ClassName(op1) += op2; isn't allowed to elide the copy for the return value).
With respect to your actual question, it is important to distinguish
between references and values:
When a value like ClassName is used, the object being passed to a
function or returned from a function is a copy. When the class
in question is trivial, that is just OK but if the class is more
complicated and allocates memory for its members there may be some
cost in creating actual copies. On the other hand, semantically it is
often necessary to use a copy. For example, the result of an addition
is normally a different object and, thus, needs to be a copy.
To take the edge out of the some of the costs, the compiler tries
hard to avoid copies when it can. For example, when returning a
local variable the copy is in many cases elided or the object is,
at least, moved (assuming C++11 or later is used and the class has
a move constructor).
When a reference like ClassName& or ClassName const& is used,
the entity is actually just a name for an object. No copy is made
but an object needs to exist somewhere. The presence of const vs.
the absence of const just indicates whether all methods or only
const methods can be called or whether a reference can be passed
where another reference is needed: a non-const reference can
be converted to a const reference but not vice verse.
For the purpose of parameters, the key difference between const
reference and non-const reference is that temporary objects
cannot bind to non-const reference while they can bind to
const references.
With that sorted, here is a run-through of the different declarations of
the operator+():
ClassName & operator+(ClassName &other)
This declaration takes a non-const reference as argument and returns
a non-const reference. For the argument this means that it can't
be a temporary but rather needs to have a name. For the return that
means that the returned object needs to be kept alive. Normally the
addition creates a new object and trying to keep an object alive
is bound to not work. If ClassName is really expensive to copy
you might not want to provide an operator+() but rather just
an operatr+=() which semantically produces the same result as
operator+() but does so in-place.
ClassName operator+(ClassName &other)
This declaration has the primary constraint that the argument is
a non-const reference, i.e., the argument cannot be a temporary
object but has to be an lvalue, i.e., something which is somehow
named. However, the arguemnt won't be copied.
ClassName operator+(const ClassName &other)
This declaration works with temporaries and is one of the two likely
candidates of how a member operator+() would look like. The argument
still isn't copied but because it is [logically] immutable temporary
objects can be used as arguments.
ClassName operator+(const ClassName other)
This will copy the argument. For the purpose of the declaration
the const will be meaningless and it can actually differ between
different declarations of the same function. When the definition
uses this declaration it means that the argument is copied, i.e., the
operator+() has a local version but it can't actually change this
copy. Most likely it will need to create another copy to produce a
result. Correspondingly, I think defining a function with a const
value argument is utterly pointless: if you want a constant, use
const ClassName& or ClassName const& (these are identical; I do
prefer the latter notation as it results in consistent placement).
ClassName operator+(ClassName other)
Like the previous declaration this one copies the argument but the
argument is mutable. Assuming the oepration is commutative, you can
readily mutate other to produce the return value. Note, however,
that copying other won't be elided (the compiler is not allowed
to elide copying function parameters) but it can be moved. Assuming
ClassName is either cheap to copy or other is used as the basis
for the result, this is the other likely candidate how the operator
is defined.
First one returns a reference, and takes a reference - this means that you can theoretically change both arguments, and you return a reference, which can be changed too. This is usually not what you want to do( 2 + 3 doesn't return 5 that can be reassigned to 17).
Second one is the same, but no reference returned.
Third has const reference - a reference which can't be modified. This is usually what you want to do, since you get element fast(you get the element), but you can't change it(incidentally or not).
Fourth one is like a third, but you copy an argument, and make it const for some reason. This makes little sense.
And the last one just takes a copy.
There isn't much difference between some of them - some of them make little sense if you think about what operator+ has to do; the area where they differ is mostly how you get the argument - and most of the time, you want to get it fast, so const ClassName& other is probably your best bet.

operator= overloading return argument instead of *this

I have a theoretical question:
Usually, in an operator= implementation, it returns *this. But what happens if we instead returned *other, where other is the right hand side of the assignment?
Thanks
Reason for returning *this is to enable assignment in this form
a = b = c
this is same as
a.operator=( b.operator=(c))
If you return other, compiler wont be able to compile this kind of assignments.
The purpose of returning *this from the assignment operator is to allow for assignment chaining, e.g.
int x = 2;
int y = x = 5; // Equivalent to: 'int y = (x = 5);'
The copy assignment operator is usually declared as:
T& operator=(const T& other);
Here the argument other is declared as const and can thus not be returned. Returning by const T& will also act differently as the caller can not assign to the returned reference, thus disallowing assignment chaining.
Often you will also assign with a temporary. If you customize the behaviour of the assignment operator to return a reference to this temporary it can result in dangling references and other dangerous behaviours.
std::string s;
(s = "abc") = "def"; // Can't assign to rhs.
It is possible to customize the behaviour of the assignment operator to achieve different behaviours, but you should generally refrain from doing so to keep the meaning of the operator clear. If you want custom behaviour it's better to provide a function with a good descriptive name.
More info about operator overloading can be found here.
Well their will be two factual differences :
You will be forced to return a reference to a const instance from your operator= or a copy
You assignment will return a reference on its right hand side instead of its left hand side
Mostly what this means is you will surprise some developers that might exploit the return value of your assignment thinking it gives back the left hand side as most objects do.
But there is not so many people who sanely intend to write things like:
(a = b).do_action();
And since you just assigned the right value to the left one and you are returning either a const instance or a copy, most of the operations will most likely result in the same thing no matter if they are called on the left or right instance. So basically most of the time it won't change anything to your life.
However except if you are a boost::spirit developer, you are highly encouraged to avoid such things for the sanity of your colleagues :)

Confusing C++ code involving *this?

Could someone explain this code? I don't understand line 3:
MyString MyString::operator+(const MyString &str)
{
MyString ss(*this); //----> explain this part
ss += str;
return ss;
}
Thanks!
This code:
MyString ss(*this);
Says "declare a new variable of type MyString that's named ss, and initialize it as a copy of *this." Inside of a member function, this is a pointer to the receiver object (the object that the member function is acting on), so *this is a reference to the receiver object. Consequently, you can read this as "make a new MyString that's called ss and is a copy of the receiver object."
The idiom being used here is implementing operator + in terms of operator +=. The idea is to make a copy of the receiver object, use operator += to add the parameter to the copy, and then to return the copy. It's a widely-used trick that simplifies the implementation of freestanding operators given implementation of the corresponding compound assignment operator.
Hope this helps!
This is an instance of the common code reuse technique to implement one operator in terms of another.
Suppose we have already defined a compound-plus operator:
class X
{
X & operator+=(const X&);
};
This unary operator allows us to write a += b, it modifies a and returns a reference to itself. This is all fine and good. Now if we also want to provide a copying, binary plus opearator a + b, which returns a the new value by value and leaves both a and b unchanged, then we want to take advantage of the code we've already written for the compound operator. We do so by calling the unary operator on a temporary copy of a:
X X::operator+(const X & b) const { return X(*this) += b; }
^^^^^^^^
temporary copy
This is exactly what your code does, only a bit more verbosely. You could as well have written return MyString(*this) += str;
There are other idioms with follow a similar spirit, such as implementing non-const access in terms of const access, copy-assign in terms of copy-construct and swap, and move-assign in terms of move-construct and swap. It always boils down to avoiding code duplication.
It's a constructor for MyString that takes the value of the current object (of type MyString) as its argument.
From what I can tell that is a copy constructor which creates a new MyString with the contents of the current MyString object.
ss is a new string which gets constructed with a copy constructor from the original string.

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

In C++, the concept of returning reference from the copy assignment operator is unclear to me. Why can't the copy assignment operator return a copy of the new object? In addition, 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:
A A::operator=(const A& a)
{
if (this == &a)
{
return *this;
}
param = a.param;
return *this;
}
Strictly speaking, the result of a copy assignment operator doesn't need to return a reference, though to mimic the default behavior the C++ compiler uses, it should return a non-const reference to the object that is assigned to (an implicitly generated copy assignment operator will return a non-const reference - C++03: 12.8/10). I've seen a fair bit of code that returns void from copy assignment overloads, and I can't recall when that caused a serious problem. Returning void will prevent users from 'assignment chaining' (a = b = c;), and will prevent using the result of an assignment in a test expression, for example. While that kind of code is by no means unheard of, I also don't think it's particularly common - especially for non-primitive types (unless the interface for a class intends for these kinds of tests, such as for iostreams).
I'm not recommending that you do this, just pointing out that it's permitted and that it doesn't seem to cause a whole lot of problems.
These other SO questions are related (probably not quite dupes) that have information/opinions that might be of interest to you.
Has anyone found the need to declare the return parameter of a copy assignment operator const?
Overloading assignment operator in C++
A bit of clarification as to why it's preferable to return by reference for operator= versus return by value --- as the chain a = b = c will work fine if a value is returned.
If you return a reference, minimal work is done. The values from one object are copied to another object.
However, if you return by value for operator=, you will call a constructor AND destructor EACH time that the assignment operator is called!!
So, given:
A& operator=(const A& rhs) { /* ... */ };
Then,
a = b = c; // calls assignment operator above twice. Nice and simple.
But,
A operator=(const A& 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 sum, there is nothing gained by returning by value, but a lot to lose.
(Note: This isn't meant to address the advantages of having the assignment operator return an lvalue. Read the other posts for why that might be preferable)
When you overload operator=, you can write it to return whatever type you want. If you want to badly enough, you can overload X::operator= to return (for example) an instance of some completely different class Y or Z. This is generally highly inadvisable though.
In particular, you usually want to support chaining of operator= just like C does. For example:
int x, y, z;
x = y = z = 0;
That being the case, you usually want to return an lvalue or rvalue of the type being assigned to. That only leaves the question of whether to return a reference to X, a const reference to X, or an X (by value).
Returning a const reference to X is generally a poor idea. In particular, a const reference is allowed to bind to a temporary object. The lifetime of the temporary is extended to the lifetime of the reference to which it's bound--but not recursively to the lifetime of whatever that might be assigned to. This makes it easy to return a dangling reference--the const reference binds to a temporary object. That object's lifetime is extended to the lifetime of the reference (which ends at the end of the function). By the time the function returns, the lifetime of the reference and temporary have ended, so what's assigned is a dangling reference.
Of course, returning a non-const reference doesn't provide complete protection against this, but at least makes you work a little harder at it. You can still (for example) define some local, and return a reference to it (but most compilers can and will warn about this too).
Returning a value instead of a reference has both theoretical and practical problems. On the theoretical side, you have a basic disconnect between = normally means and what it means in this case. In particular, where assignment normally means "take this existing source and assign its value to this existing destination", it starts to mean something more like "take this existing source, create a copy of it, and assign that value to this existing destination."
From a practical viewpoint, especially before rvalue references were invented, that could have a significant impact on performance--creating an entire new object in the course of copying A to B was unexpected and often quite slow. If, for example, I had a small vector, and assigned it to a larger vector, I'd expect that to take, at most, time to copy elements of the small vector plus a (little) fixed overhead to adjust the size of the destination vector. If that instead involved two copies, one from source to temp, another from temp to destination, and (worse) a dynamic allocation for the temporary vector, my expectation about the complexity of the operation would be entirely destroyed. For a small vector, the time for the dynamic allocation could easily be many times higher than the time to copy the elements.
The only other option (added in C++11) would be to return an rvalue reference. This could easily lead to unexpected results--a chained assignment like a=b=c; could destroy the contents of b and/or c, which would be quite unexpected.
That leaves returning a normal reference (not a reference to const, nor an rvalue reference) as the only option that (reasonably) dependably produces what most people normally want.
It's partly because returning a reference to self is faster than returning by value, but in addition, it's to allow the original semantics that exist in primitive types.
operator= can be defined to return whatever you want. You need to be more specific as to what the problem actually is; I suspect that you have the copy constructor use operator= internally and that causes a stack overflow, as the copy constructor calls operator= which must use the copy constructor to return A by value ad infinitum.
There is no core language requirement on the result type of a user-defined operator=, but the standard library does have such a requirement:
C++98 §23.1/3:
” The type of objects stored in these components must meet the requirements of CopyConstructible
types (20.1.3), and the additional requirements of Assignable types.
C++98 §23.1/4:
” In Table 64, T is the type used to instantiate the container, t is a value of T, and u is a value of (possibly const) T.
Returning a copy by value would still support assignment chaining like a = b = c = 42;, because the assignment operator is right-associative, i.e. this is parsed as a = (b = (c = 42));. But returning a copy would prohibit meaningless constructions like (a = b) = 666;. For a small class returning a copy could conceivably be most efficient, while for a larger class returning by reference will generally be most efficient (and a copy, prohibitively inefficient).
Until I learned about the standard library requirement I used to let operator= return void, for efficiency and to avoid the absurdity of supporting side-effect based bad code.
With C++11 there is additionally the requirement of T& result type for default-ing the assignment operator, because
C++11 §8.4.2/1:
” A function that is explicitly defaulted shall […] have the same declared function type (except for possibly differing 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 member function’s class) as if it had been implicitly declared
I guess, because user defined object should behave like builtin types.
For example:
char c;
while ((c = getchar()) != -1 ) {/* do the stuff */}
Copy assignment should not be void, otherwise assignment chain will not work
a = b = c;
// because assignment operator is right-associative
// it is equal to
a = (b = c); // oops, (b = c) return nothing, the code won't compile
Copy assignment should not return a value, otherwise unnecessary copy constructor and destructor will be called
// suppose a, b and c are of type X, which holds some resource that will take efforts to copy
a = b = c;
// is equal to
X temp1.X::( (b = c) ); // copy constructor called once
X temp2.X::( a.X::operator=(temp1) ); // copy constructor called twice; temp1 destructed inside a.X::operator=(temp1)
Copy assignment should not return rvalue reference cos it may have the assigned object moved. Again take the assignment chain for example
a = b = c;
// if a has a copy assignment overload that takes rvalue reference as argument like the following
X& operator=(X &&);
// then the result of (b = c) will be moved into a, and make b an invalid object afterwards

Overloading assignment operator in C++

As I've understand, when overloading operator=, the return value should should be a non-const reference.
A& A::operator=( const A& )
{
// check for self-assignment, do assignment
return *this;
}
It is non-const to allow non-const member functions to be called in cases like:
( a = b ).f();
But why should it return a reference? In what instance will it give a problem if the return value is not declared a reference, let's say return by value?
It's assumed that copy constructor is implemented correctly.
Not returning a reference is a waste of resources and a yields a weird design. Why do you want to do a copy for all users of your operator even if almost all of them will discard that value?
a = b; // huh, why does this create an unnecessary copy?
In addition, it would be surprising to users of your class, since the built-in assignment operator doesn't copy likewise
int &a = (some_int = 0); // works
A good general advice when overloading operators is 'do as primitive types do', and the default behavior of assignment to a primitive type is that.
Not returning anything could be an option, to disable assignment inside other expressions if you feel the need, but returning a copy does not make sense at all: if the caller wants to make a copy they can make it out of the reference, if they do not need the copy there is no need to generate a temporary that is not needed.
Cause f() can modify a. (we return a non-const reference)
If we return a value (a copy) of a, f() will modify the copy, not a
I'm not sure how often you'd want to do it, but something like: (a=b)=c; requires a reference to work.
Edit: Okay, there is a bit more to it than that. Much of the reasoning is semi-historical. There's more reason you don't want to return an rvalue than just avoiding an unnecessary copy into a temporary object. Using a (minor) variation on an example originally posted by Andrew Koenig, consider something like this:
struct Foo {
Foo const &assign(Foo const &other) {
return (*this = other);
}
};
Now, assume you're using an old version of C++ where assignment returned an rvalue. In that case, (*this=other); will yield that temporary. You're then binding a reference to the temporary, destroying the temporary, and finally returning a dangling reference to the destroyed temporary.
Rules that have been enacted since (extending the life of a temporary used to initialize a reference) would at least mitigate (and might completely cure) this problem, but I doubt anybody re-visited this particular situation after those rules had been written. It was a bit like an ugly device driver that includes kludges to work around dozens of bugs in different versions and variations of the hardware -- it could probably be refactored and simplified, but nobody's quite sure when some seemingly innocuous change will break something that currently works, and ultimately nobody wants to even look at it if they can help it.
If your assignment operator does not take a const reference parameter:
A& A::operator=(A&); // unusual, but std::auto_ptr does this for example.
or if the class A has mutable members (reference count?), then it is possible that the assignment operator changes the object being assigned from as well as assigned to. Then if you had code like this:
a = b = c;
The b = c assignment would occur first, and return a copy (call it b') by value instead of returning a reference to b. When the a = b' assignment is done, the mutating assignment operator would change the b' copy instead of the real b.
Another potential problem -- returning by value instead of by reference could cause slicing if you have virtual assignment operators. I'm not saying that's a good idea, but it could be a problem.
If you intend to do something like (a = b).f() then you will want it to return by reference so that if f() mutates the object, it is not mutating a temporary.
In real code (i.e. not things like (a=b)=c), returning a value is unlikely to cause any compile errors, but it is inefficient to return a copy because creating a copy can often be expensive.
You can obviously come up with situation where a reference is needed, but those rarely -- if ever -- come up in practice.
This is Item 10 of Scott Meyers' excellent book, Effective C++. Returning a reference from operator= is only a convention, but it's a good one.
This is only a convention; code that doesn't follow it will compile. However, the convention is followed by all the built-in types as well as by all the types in the standard library. Unless you have a good reason for doing things differently, don't.
If you're worried that returning the wrong thing might silently cause unintended side effects, you could write your operator=() to return void. I've seen a fair bit of code that does this (I assume out of laziness or just not knowing what the return type should be rather than for 'safety'), and it causes few problems. The kind of expressions that need to use the reference normally returned by operator=() are pretty rarely used, and almost always easy code an alternative for.
I'm not sure I'd endorse returning void (in a code review I'd probably call it out as something you shouldn't do), but I'm throwing it out there as an option to consider if you want to not have to worry about how oddball uses of the assignment operator might be handled.
late edit:
Also, I should have originally mentioned that you can split the difference by having your operator=() return a const& - that will still permit assignment chaining:
a = b = c;
But will disallow some of the more unusual uses:
(a = b) = c;
Note that this makes the assignment operator have semantics similar to what it has in C, where the value returned by the = operator is not an lvalue. In C++, the standard changed it so the = operator returns the type of the left operand, so it is an lvalue, but as Steve Jessop noted in a comment to another answer, while that makes it so the compiler will accept
(a = b) = c;
even for built-ins, the result is undefined behavior for built-ins since a is modified twice with no intervening sequence point. That problem is avoided for non-builtins with an operator=() because the operator=() function call is a sequence point.
Returning by reference reduces the time of performing chained operations. E. g. :
a = b = c = d;
Let's see the actions which would be called, if operator= returns by value.
Copy assignment opertor= for c makes c equal to d and then creates temporary anonymous object (calls copy ctor). Let's call it tc.
Then operator= for b is called. Right hand side object is tc. Move assignment operator is called. b becomes equal to tc. And then function copies b to temporary anonymous, let's call it tb.
The same thing happens again, a.operator= returns temporary copy of a. After operator ; all three temporary objects are destroyed
Altogether: 3 copy ctors, 2 move operators, 1 copy operator
Let's see what will change if operator= will return value by reference:
Copy assignment operator is called. c becomes equal to d, reference to lvalue object is returned
The same. b becomes equal to c, reference to lvalue object is returned
The same. a becomes equal to b, reference to lvalue object is returned
Altogether: only three copy operators is called and no ctors at all!
Moreover I recommend you to return value by const reference, it won't allow you to write tricky and unobvious code. With cleaner code finding bugs will be much easier :) ( a = b ).f(); is better to split to two lines a=b; a.f();.
P.S.:
Copy assignment operator : operator=(const Class& rhs).
Move assignment operator : operator=(Class&& rhs).
If it returned a copy, it would require you to implement the copy constructor for almost all non-trivial objects.
Also it would cause problems if you declare the copy constructor private but leave the assignment operator public... you would get a compile error if you tried to use the assignment operator outside of the class or its instances.
Not to mention the more serious problems already mentioned. You don't want it to be a copy of the object, you really do want it to refer to the same object. Changes to one should be visible to both, and that doesn't work if you return a copy.