Overloading compound assignment operators in C++ - c++

Why is it necessary when we overload the += operator to return a reference to the class?
why the prototype is for instance ClassName& operator += (const ClassName& instance) instead of
void operator += (const ClassName& instance) ?

Several reasons.
That's what the builtin += and friends do; your overloaded version better act the same without a really good reason and documentation of the fact, or else people who use it expecting it to act like the built in ones will get upset.
The builtin ones act that way to allow them to be used as part of a larger expression like a + (b += c).
It makes defining an overloaded operator+() in terms of operator+=() and a copy constructor easy. Consider
class foo {
foo& operator+=(const foo &b) {
// Do stuff
return *this;
}
};
foo operator+(foo a, const foo &b) { // First argument passed by value, second by reference
return a += b;
}
Making the compound operators class members and the plain operators freestanding is a common style suggestion; it allows the first argument to be of a different type as long as it can be converted to a foo: foo b; foo a = 1 + b;
(All examples are very contrived for demonstration purposes)

It is because the operator += has to be a left-hand-expression.
For instance, in a += ...; there is no way the result of the evaluation of this expression won't be a left-hand-expression (i.e., a += b + c + d;).

Related

What type does the conversion logic target?

I don't understand why in the following code the expression C c3 = 5 + c;
doesn't get compiled although 5 could be converted to type C as in the previous statement.
#include <iostream>
class C
{
int m_value;
public:
C(int value): m_value(value) {} ;
int get_value() { return m_value; } ;
C operator+(C rhs) { return C(rhs.m_value+m_value); }
};
int main()
{
C c = 10;
C c2 = c + 5; // Works fine. 5 is converted to type C and the operator + is called
C c3 = 5 + c; // Not working: compiler error. Question: Why is 5 not converted to type C??
std::cout << c.get_value() << std::endl; // output 10
std::cout << c2.get_value() << std::endl; // output 15
}
Because if overload operator as member function of the class, it could only be called when the object of that class is used as left operand. (And the left operand becomes the implicit *this object for the member function to be called.)
Binary operators are typically implemented as non-members to maintain symmetry (for example, when adding a complex number and an integer, if operator+ is a member function of the complex type, then only complex+integer would compile, and not integer+complex).
From the standard, [over.match.oper]/3
(emphasis mine)
For a unary operator # with an operand of a type whose cv-unqualified
version is T1, and for a binary operator # with a left operand of a
type whose cv-unqualified version is T1 and a right operand of a type
whose cv-unqualified version is T2, four sets of candidate functions,
designated member candidates, non-member candidates, built-in
candidates, and rewritten candidates, are constructed as follows:
(3.1) If T1 is a complete class type or a class currently being defined, the set of member candidates is the result of the qualified
lookup of T1::operator# ([over.call.func]); otherwise, the set of
member candidates is empty.
That means if the type of left operand is not a class type, the set of member candidates is empty; the overloaded operator (as member function) won't be considered.
You can overload it as a non-member function to allow the implicit conversion for both left and right operands.
C operator+(C lhs, C rhs) { return C(lhs.get_value() + rhs.get_value()); }
then both c + 5 or 5 + c would work fine.
LIVE
BTW: This will cause one temporaray object being constructed (from int to C) for the non-member function to be called; if you care about that, you can add all the three possible overloads as follows. Also note that this is a trade-off issue.
C operator+(C lhs, C rhs) { return C(lhs.get_value() + rhs.get_value()); }
C operator+(C lhs, int rhs) { return C(lhs.get_value() + rhs); }
C operator+(int lhs, C rhs) { return C(lhs + rhs.get_value()); }
And here're some suggestions about when to use a normal, friend, or member function overload.
In most cases, the language leaves it up to you to determine whether
you want to use the normal/friend or member function version of the
overload. However, one of the two is usually a better choice than the
other.
When dealing with binary operators that don’t modify the left operand
(e.g. operator+), the normal or friend function version is typically
preferred, because it works for all parameter types (even when the
left operand isn’t a class object, or is a class that is not
modifiable). The normal or friend function version has the added
benefit of “symmetry”, as all operands become explicit parameters
(instead of the left operand becoming *this and the right operand
becoming an explicit parameter).
When dealing with binary operators that do modify the left operand
(e.g. operator+=), the member function version is typically preferred.
In these cases, the leftmost operand will always be a class type, and
having the object being modified become the one pointed to by *this is
natural. Because the rightmost operand becomes an explicit parameter,
there’s no confusion over who is getting modified and who is getting
evaluated.
You are facing the reason to define certain operator overloads as free functions, i.e., when implicit conversions are desired. To see what's going on under the hood, consider the verbose form of operator overload invocations:
C c2 = c.operator+(5); // Ok, c has this member function
C c3 = 5.operator+(c); // No way, this is an integer without members
You can obviously do is an explicit C construction as in
C c3 = C{5} + c;
but this is not intended for an arithmetic value type like C. To make the implicit construction possible, define the overload as a free function
auto operator + (C lhs, const C& rhs)
{
lhs += rhs;
return lhs;
}
Now, there is no restriction of the left hand side operand. Note that the operator is implemented in terms of += (you would have to implement it to make the above code compile), which is good practice as pointed out in this thread: when you provide a binary operator + for a custom type, users of that type will also expected operator += to be available. Hence, to reduce code duplication, it's usually good to implement + in terms of += (same for all other arithmetic operands).
Further note that these operands often require a substantial amount of boilerplate code. To reduce this, consider e.g. the Boost operators library. To generate all standard arithmetic operators based on the minimal amount of actual hand-written code:
#include <boost/operators.hpp>
class C : private boost::arithmetic<C>
// ^^^^^^^^^^^^^^^^^^^^
// where the magic happens (Barton-Nackmann trick)
{
int m_value ;
public:
C(int value): m_value(value) {} ;
C& operator+=(const C& rhs) { m_value += rhs.m_value; return *this; }
C& operator-=(const C& rhs) { m_value -= rhs.m_value; return *this; }
C& operator*=(const C& rhs) { m_value *= rhs.m_value; return *this; }
C& operator/=(const C& rhs) { m_value /= rhs.m_value; return *this; }
const C& operator+() const { return *this; }
C operator-() const { return {-m_value}; }
int get_value() { return m_value; } ;
};
Here's an additional remark (a bit of a "reductio ad absurdum") on why your suggestion that the compiler could implicitly convert the left hand argument to a C would, essentially, open a can of worms. The actual language rules say, simply put, that, before applying conversions, a name lookup – for function calls and calls to (user-declared) operators – is done to find a candidate set. At this point, the operand types are not yet considered, but the scope very well is. So the type of the first argument does matter insofar as a user-declared operator is only in scope if its first argument is of the (cv-qualified) class type it is declared in. When a candidate set has been found, the compiler then tries to apply the conversion rules and ranks the candidates etc.
(Your question is therefore a bit misleading because in your example, we don't even get to the conversion logic, instead name resolution already comes up empty.)
Now, imagine we could simply change the language to say that the first argument can also be converted, prior to name resolution. A little bit of handwaving is required here, because this means we have to do conversions, look up names, and then do conversions again, so how this would work in practice is certainly unclear. Anyway, look at this example then:
struct B;
struct A
{
A(int);
A operator +(B) const;
};
struct B
{
B(int);
B operator +(B) const;
};
Now, what should 1 + B{3} do? Apparently, it could be transformed to B{1} + B{3}. But who's to say we couldn't do A{1} + B{3} instead? Why would B's constructor be preferred over A's? Of course, we could argue that either B is to be preferred, because, look at how nice and symmetric B{...}+B{...} is (ok, I'm being slightly facetious). Or we could take the safer route of saying that the program is ill-formed if it contains such an ambiguity. But there are a lot more corner cases to consider, e.g. what if B's constructor was made explicit – should the compiler (still or newly) error out, or should it silently switch to the usable implicit conversion to A?
Another non-obvious point is which types in which scopes (e.g. namespaces) should be considered? It would certainly be surprising if you use operator + in e.g. global namespace scope, and the compiler would dig out some type __gnucxx::__internal::__cogwheels::__do_something_impl, implcitly convert an operand to it, and then perform an operation on that.
Also note that this feature even if it can be specified in a reasonable and clean manner, could have quite a compile-time cost (in fact, overload resolution is already one of the biggests costs when compiling C++ and one of the reasons why compiling C++ code can take a lot longer than compiling C).
TL;DR:
There are tricky corner cases.
The benefit is marginal (why not make such operators free functions as others have pointed out)?
The discussions on how to standardize this would certainly be long.

Are arithmetic/assignment operators and compound assignment operators independently defined in C++

I.e if within a class definition I overload operator+ or operator= does this have any effect on the operator+=? and vice versa.
Or are these operators completely independent unless otherwise defined?
No, these operators are completely independent.
You can of course implement one using the others, but by default they're independent.
struct X
{
X& operator = (const X&);
X operator + (const X&) const;
//X& operator += (const X& other)
// { operator=(operator+(other)); return *this; }
};
X x, y;
x += y; //doesn't compile unless you uncomment that line
The language imposes no restriction about this - you could have an operator + that sums two objects and a += that blows up the sun and it would still be legal. On the other hand, it's strongly advised not to come up with counterintuitive operator overloads, otherwise your class would result extremely awkward to use.
Incidentally, to avoid code duplication often + is implemented in terms of +=:
A operator+(const A& right) const
{
A ret(*this);
ret+=right;
return ret;
}
No, if you what that behavior, you also need to override += operator!

operator overloading in c++

If I want to overload operator +, which prototype is correct?
D operator+(const D& lhs, const D& rhs);
then declare it as a friend function of D.
D operator+(const D& s);
Then declare it as a member function of D.
The first is correct, the second is plain wrong. You can improve the second by writing this
D operator+(const D& s) const;
but it's still wrong. The reason is that the compiler will apply different rules to the left and right hand sides of your + operator in the second version. For instance given this code
class C
{
};
class D
{
public:
D(const C&);
};
C c;
D d;
d = d + c; // legal with both versions
d = c + d; // not legal with the second version
The difference is because the compiler will create a temporary D object from a C object for a method or function argument but it won't do it to make a method call on the temporary object.
In short the first version treats the left hand side and right hand side equally and so agrees better with the coders expectations.
The second one should be
D operator+(const D& s) const;
Then either is good.
As to the first needing to be a friend: only if it really needs access to anything private. Normally you can implement it in terms of the public interface, commonly with the corresponding operator+=:
D operator+(D lhs, const D& rhs) { //copy left-hand side
return lhs += rhs; //add the right-hand side and return by value
}
I would advice you to follow a third path: Implement operator+= as a member function, and then implement operator+ in terms of the previous like:
D operator+=( D lhs, D const & rhs ) {
lhs += rhs;
return lhs;
}
The advantage of the third way is that with basically the same code you provide both + and +=, and you get to implement operator+ as a free function which is an advantage from the point of view of symmetry, if your class has implicit conversions, it will allow d + t and t + d for any object d of type D and any other object t of type implicitly convertible to D. The member function version will only apply conversions to the right hand side, which means that d + t will be allowed, but not t + d.
[self publicity warning] You can read a longer explanation on this particular issue here
Go with the first one. However, if it needs to access private members,only then make it friend, otherwise make it non-friend function.
I think both are correct. But one thing you missed (that may or may not apply), is that if the left side value can be something other than D (say an integer or something) then option 1 works for that e.g.
D operator+(int lhs, const D& rhs);
Then you can do something like:
D d1;
D d2 = 5 + d1;
You only need to declare a function as a friend of a class if you plan to access private variables of the class through this function. In the first prototype you do not need to declare the function as a friend since you are passing in all the values you plan to use. In the second prototype you can declare the function a friend since you will need to access one more variable, but it is better practice and easier to just declare the function as a member.
Both methods are almost correct. It's just two ways of doing almost the same. But when you need to apply the binary operator to other types other than D (for example int+D) you need to use the second one.
The option 1 does not even have to be a friend if it does not need access to the private members.
The option 2 have to be fixed a little. You are missing D::.
D D::operator+(const D& s) const {
}
The principle of least surprise says that your overload should behave
more or less like the built-in operators. The usual solution here is
not to implement operator+ at all, but to implement:
D& operator+=( D const& rhs );
as a member, and then derive from something like:
template<typename T>
class ArithmeticOperators
{
friend T operator+( T const& lhs, T const& rhs )
{
T result( lhs );
result += rhs;
return result;
}
// Same thing for all of the other binary operators...
};
This way, you don't have to rewrite the same thing every time you define
a class which overloads the arithmetic operators, and you're guaranteed
that the semantics of + and += correspond.
(The friend in the above is simply to allow you to put the function,
along with its implementation, in the class itself, where ADL will find
it.)
Both are correct (after fixing the const correctness problem others pointed out), but I recommend the member operator. It can be overridden with polymorphic behavior if virtual, have better cohesion with the class and as a result it is easier to maintain and to document. Try to avoid friend functions if you can.
As for the "derived = base + derived" case, I recommend not mixing value semantics operators and polymorphism, it can have unforeseen consequences due to various implicit conversion sequences and object slicing. That example might be equivalent to derived = Derived(base) + derived, but it can be derived = Derived(base + Base(derived)) as well if base has operator +.
Use only explicit conversion and casting, and you will not encounter any mysterious strange behavior. And think twice before you implement operators for a polymorphic class.
The first one has a different behavior if D has implicit constructors.
Consider
struct D
{
D(int);
};
D operator+(const D&, const D&);
Then you can do 1 + d and d + 1, which you cannot do with the member operator+ (the lhs must be a D, and no conversion shall take place).

Why can't we overload "=" using friend function?

Why it is not allowed to overload "=" using friend function?
I have written a small program but it is giving error.
class comp
{
int real;
int imaginary;
public:
comp(){real=0; imaginary=0;}
void show(){cout << "Real="<<real<<" Imaginary="<<imaginary<<endl;}
void set(int i,int j){real=i;imaginary=j;}
friend comp operator=(comp &op1,const comp &op2);
};
comp operator=(comp &op1,const comp &op2)
{
op1.imaginary=op2.imaginary;
op1.real=op2.real;
return op1;
}
int main()
{
comp a,b;
a.set(10,20);
b=a;
b.show();
return 0;
}
The compilation gives the following error :-
[root#dogmatix stackoverflow]# g++ prog4.cpp
prog4.cpp:11: error: ‘comp operator=(comp&, const comp&)’ must be a nonstatic member function
prog4.cpp:14: error: ‘comp operator=(comp&, const comp&)’ must be a nonstatic member function
prog4.cpp: In function ‘int main()’:
prog4.cpp:25: error: ambiguous overload for ‘operator=’ in ‘b = a’
prog4.cpp:4: note: candidates are: comp& comp::operator=(const comp&)
prog4.cpp:14: note: comp operator=(comp&, const comp&)
Because if you do not declare it as a class member compiler will make one up for you and it will introduce ambiguity.
The assignment operator is explicitly required to be a class member operator. That is a sufficient reason for the compiler to fail to compile your code. Assignment is one of the special member functions defined in the standard (like the copy constructor) that will be generated by the compiler if you do not provide your own.
Unlike other operations that can be understood as external to the left hand side operator, the assignment is an operation that is semantically bound to the left hand side: modify this instance to be equal to the right hand side instance (by some definition of equal), so it makes sense to have it as an operation of the class and not an external operation. On the other hand, other operators as addition are not bound to a particular instance: is a+b an operation of a or b or none of them? -- a and b are used in the operation, but the operation acts on the result value that is returned.
That approach is actually recommended and used: define operator+= (that applies to the instance) as a member function, and then implement operator+ as a free function that operates on the result:
struct example {
example& operator+=( const example& rhs );
};
example operator+( const example& lhs, const example& rhs ) {
example ret( lhs );
ret += rhs;
return ret;
}
// usually implemented as:
// example operator+( example lhs, const example& rhs ) {
// return lhs += rhs; // note that lhs is actually a copy, not the real lhs
//}
Assignment(=) operator is a special operator that will be provided by the constructor to the class when programmer has not provided(overloaded) as member of the class.(like copy constructor).
When programmer is overloading = operator using friend function, two = operations will exists:
1) compiler is providing = operator
2) programmer is providing(overloading) = operator by friend function.
Then simply ambiguity will be created and compiler will gives error. Its compilation error.
There is no good reason for that, I think Stepanov proposed that there should be a free operator= and many good stuff can be done with that (even more than what can be done with the move assignment). I can't find the citation but Stepanov went as a far as to suggest that the constructors could be free functions http://www.stlport.org/resources/StepanovUSA.html.
There is a way around it, which is to systematically declare a template<class Other> A& operator=(Other const& t); in inside all your classes, in this way you leave the option to anyone to define a custom assignment operator.
Of course you can't do this with a class you don't have control over.
Having said that it is nowadays not that bad since we have move assignment. And in some sense conversion operators B::operator A() const{...} are almost like a custom copy assignment. Conversion operators are now usable because of explicit. However you have to have control over the second type (B), the right-hand type in assignment.
Next question is why conversion operator need to be members them selves? Again, I don't think there is a good reason.

Why are operators sometimes stand-alone and sometimes class methods?

Why is that sometimes an operator override is defined as a method in the class, like
MyClass& MyClass::operatorFoo(MyClass& other) { .... return this; };
and sometimes it's a separate function, like
MyClass& operatorFoo(MyClass& first, MyClass& bar)
Are they equivalent? What rules govern when you do it one way and when you do it the other?
If you want to be able to do something like 3 + obj you have to define a free (non-member) operator.
If you want to make your operators protected or private, you have to make them methods.
Some operators cannot be free functions, e.g., operator->.
This is already answered here:
difference between global operator and member operator
If you have a binary operator like +, you normally want type conversions to be performed on both operands. For example, a string concatenation operator needs to be able to convert either or both of its operands from a char * to a string. If that is the case, then it cannot be a member function, as the left hand operand would be *this, and would not have a conversion performed.
E.g.:
operator+( a, b ); // conversions performed on a and b
a->operator+( b ); // conversion can only be performed on b
If the operator is defined outside of a class it's considered global, and allows you to have other types appear on the left hand side of the operator.
For example, given a class Foo, with a global operator + you could do:
Foo f;
Foo f2 = 55 + f;