Specific cases of operator overloading. Why do the cases matches the snippets?

This is the exercise:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct A {
	A(int a_) : m_a(a_){}

	A& operator +=(A const& rhs_) {
		m_a += rhs_.m_a;     //m_a = m_a + rhs_.m_a  //then b = a + 1?
		return *this;
	}

	A& operator -=(A const& rhs_) {
		m_a -= rhs_.m_a;
		return *this;
	}

	A operator-(A rhs_) const{	// What does this const mean?
		return rhs_ -= *this;	//rhs_ = rhs_ - *this //then 1 = 1 - d
	}
private:
	int m_a;
};

A operator+(A lhs_, A const& rhs_){
	return lhs_ += rhs_;		// b = a+1
}

int main () {
	A a(42);
	A b = a+1;   // case 1
	A c = 1+a;   // case 2
	A d = a-1;   // case 3
}


I don't understand the case 2 and 3.
- case 2 matches with the following snipped:
1
2
3
A operator+(A lhs_, A const& rhs_){
	return lhs_ += rhs_;
}

But for me as expressed in the comment the final assignation represents b = a + 1. What is wrong in my understanding?
- case 3 matches with the following snipped:
1
2
3
A operator-(A rhs_) const{	// What does this const mean?
	return rhs_ -= *this;	//rhs_ = rhs_ - *this //then 1 = 1 - d
}

But for me as expressed in the comment the final assignation represents 1=1-a. What is wrong in my understanding?
For this last case I also not understand the meaning of the "const" right before the function body definition.

Thanks.
Last edited on
Case 2 constructs a temporary and then does addition:
1
2
A tmp(1);
A c = tmp + a;  // calls tmp.operator+(a); 

If I recall correctly, the compiler will do only one of these implicit constructions to find an operator match. The rules are complex and if you ever rely on something subtle, it's a good idea to document it in a comment:
A c = 1+a; // does A(1) + a

Case 3 makes a temp and calls operator-(). It's the same as this:
1
2
A tmp(1);
A d = a - tmp;   // calls a.operator-(tmp) 

@dhayden Thanks for your answer.

I understand that a implicit conversion is done with the number "1" because the copy constructor of A is A(int a_). So the compiler make a conversion on the "1" to an object A to do the addition operation.

Then the compiler search for the operator to match.

However the returned addend order are wrong in your answer @dhayden:
- case 2 matches A c = a + tmp instead of A c = tmp + a:
1
2
3
A operator+(A lhs_, A const& rhs_){
	return lhs_ += rhs_;
}

where:
* (lhs_) attribute m_a: 42
* (rhs_) attribute m_a: 1
Then the function return: lhs_ = lhs_ + rhs.

- case 3 matches A d = tmp -a insted of A c = a - tmp:
1
2
3
	A operator-(A rhs_) const{	// What does this const mean?
		return rhs_ -= *this;	//rhs_ = rhs_ - *this -> 1 = 1 - a
	}

where:
*(rhs_) attribute m_a: 1
*(this) attribute m_a: 42
Then the function return: rhs_ = rhs_ - *this
NOTE: I also don't understand what the cons before the definition body means...

This is actually why I am puzzled because of the order of the addends.
According to my understanding the compiler should find this operator signature:
- case 2:
A& operator +=(A const& rhs_)
- case 3:
A& operator -=(A const& rhs_)

But I am clearly misunderstanding something...
Last edited on
'const' means that the object method is not allowed to modify the object. So operator-(A rhs_) const cannot modify a, b, c, or d.

The compiler will always choose the most specific choice, given the operands.

Also, the compiler is free to rearrange operands if necessary.

Hope this helps.
Also, the compiler is free to rearrange operands if necessary.

While that's true with built-in types, I'm 99% certain that it isn't true with user-defined operators. Otherwise it would change which operators get called:
1
2
3
class A a;
class B b;
a+b;  // a.operator+(b), not b.operator+(a) 


puertas12, here is a version of your code with debugging statements to show what's happening. Note that operator-() is almost certainly buggy because it returns right-left instead of left-right. I hope this helps.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
using std::cout;


struct A
{
    A(int a_):m_a(a_)
    {
	cout << "construct A(" << a_ << ")\n";
    }

    A & operator +=(A const &rhs_)
    {
	cout << m_a << "::operator+=(" << rhs_.m_a << ")\n";
	m_a += rhs_.m_a;
	return *this;
    }

    A & operator -=(A const &rhs_) {
	cout << m_a << "::operator-=(" << rhs_.m_a << ")\n";
	m_a -= rhs_.m_a;
	return *this;
    }

    A operator-(A rhs_) const
    {				// What does this const mean?
	cout << m_a << "::operator-(" << rhs_.m_a << ")\n";

	return rhs_ -= *this;	//rhs_ = rhs_ - *this //then 1 = 1 - d
    }
// private:
    int m_a;
};

A
operator+(A lhs_, A const &rhs_)
{
    cout << "operator+(" << lhs_.m_a << ',' << rhs_.m_a << ")\n";
    return lhs_ += rhs_;			 // b = a+1
}

int
main()
{
    A a(42);
    cout << "\n--- Case 1 ---\n";
    A b = a + 1;		// case 1
    cout << "\n--- Case 2 ---\n";
    A c = 1 + a;		// case 2
    cout << "\n--- Case 3 ---\n";
    A d = a - 1;		// case 3
}

construct A(42)

--- Case 1 ---
construct A(1)
operator+(42,1)
42::operator+=(1)

--- Case 2 ---
construct A(1)
operator+(1,42)
1::operator+=(42)

--- Case 3 ---
construct A(1)
42::operator-(1)
1::operator-=(42)

@dhayden Thank you for your answer. The traces are really well placed!

Now I see better what the program does but I still have two questions:
1.- Why does the Case 1 matches the signature of the operator
A & operator +=(A const &rhs_)?
For me Case 1 also matches
operator+(A lhs_, A const &rhs_)
Does overloading the operator "+" inside the class have preference than outside?

2.- What can be the reason that operator-() is buggy?
Last edited on
For me Case 1 also matches
operator+(A lhs_, A const &rhs_)

Case 1 does match operator+. You're seeing operator+= because operator+ calls operator+=:
return lhs_ += rhs_;
Does overloading the operator "+" inside the class have preference than outside?

No it's the opposite. It's preferable to overload outside the class because that way either operand can be converted to type A. If you defined operator+() as a class member then only the right hand operand could be converted, so 1+a would generate a compiler error.
What can be the reason that operator-() is buggy?

I'm not sure if you're asking "what is the bug" or "why is the bug there" so I'll answer both. The bug is that A d = a-1; results in d.m_a = -41 instead of +41. As for why the bug is there, you'd have to ask the author of the code. Maybe they are demonstrating some point, or maybe it's just an oversight.
@dhayden Thanks for your answer :). I guess I am about to finish with this topic ':/.

I think then that my problem is to know to what operand belongs this->m_a and rhs_.m_a for the case of the subtraction (following the sequential execution):

1.- A d = a - 1

2.- A operator-(A rhs_)
rhs_.m_a = 1
this->m_a = 42

3.- A & operator -=(A const &rhs_)
this->m_a = 1
rhs_.m_a = 42

//Why is the current object 1 here? I thought the current object was always 42 "A a" because it was constructed explicitly.



Last edited on
A d = a - 1;
a-1 matches a.operator-(A(1));
So it calls A(1) to construct a temporary and then calls a.operator-() with rhs_ equal to the temporary. In other words, upon entering operator-(), we have:
this == &a, so this->m_a == 42
rhs_.m_a == 1

Now what happens inside operator-()? return rhs_ -= *this;

So that calls rhs_.operator-=(*this). Look at that carefully, it's rhs_.operator-=(), not this->operator-=(), so upon entering operator-=() we have:
this == &rhs_ from operator-(). so this->m_a == 1
rhs_ == this from operator-(). So rhs_.m_a == 42

operator-=() does this->m_a -= rhs.m_a; So this->m_a = 1-42 = -41
So that's what gets returned from operator -=, and that's what gets returned from operator-()

OK! I got it. Thank you very much =)

And when we give two input arguments in the overloading operator declaration... I assume they are placed 1st input argument (left operand) 2nd input argument (right operand)

So for the A b = a + 1
a+1 matches a.operator+(A(42)+A(1))






Last edited on
Not quite. Look closely at the class definition. There is no member operator+() (although there is a member operator-(). There is only a global operator+() that takes two A arguments. So for
A b = a + 1;
a+1 matches operator+(a, A(1))
where A(1) means that the compiler generates a temporary instance of A that is initialize from 1.
--- Case 1 ---
construct A(1)   // this is constructing the temporary
operator+(42,1)            // this is operator+(a,temporary)
42::operator+=(1)  // this is operator+() calling operator+= 
I understand i.e: operator+() is a free function and operator-() is a member function.

@dhayden Thanks for all your answers =).
Topic archived. No new replies allowed.