operator+ cannot take ref parameter?

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>

using namespace std;

class Rational
{
public:
	Rational(const int n=0, const int d=1) : numer(n), denom(d) {
		printf("ctor before reduce: n(%d), d(%d) \n", numer, denom);
		reduce();
		printf("ctor after reduce: n(%d), d(%d) \n", numer, denom);
	}
	void display(const char *message = "display()") {
		printf("%s -- numer(%d) denom(%d)\n", message, numer, denom); 
	}
	//*********************** problem ***********************************
	//Rational operator+( const Rational & r2) {	//this works
	//Rational operator+(	    Rational & r2) {    //compile error *****
	//Rational operator+( const Rational   r2) {	//this works
	//Rational operator+(       Rational   r2) {	//this works
	Rational operator+( Rational & r2) {
		Rational temp;
		printf("adding: %d/%d + %d/%d \n", numer, denom, r2.numer, r2.denom);
		temp.numer = numer*r2.denom + denom*r2.numer;
		temp.denom = denom*r2.denom;
		return temp;
	}
	void reduce() {
		printf("can we reduce? numer(%d) denom(%d) \n", numer, denom);
		for (int i=0; i<9; i++) {
			if ( !(numer % divArray[i]) && !(denom % divArray[i]) ) {
				printf("divide numer(%d) and denom(%d) by (%d) \n", 
					numer, denom, divArray[i]); 
				numer /= divArray[i];
				denom /= divArray[i];
				i--;
			}
		}
	}
private:
	static int divArray[10];
	int numer;
	int denom;
};

int Rational::divArray[10]={2,3,5,7,11,13,17,19,23,29};

int main()
{
	Rational mc1(6,14);
	Rational mc2(3,9);
	Rational mc3(16, 32);

	mc1.display("mc1");

	mc2.display("mc2");

	mc3.display("mc3 before operator +");
	mc3 = mc1 + mc2;
	mc3.display("mc3 after  operator +");

	int intVar1 = 3;
	mc3 = intVar1;

	//******************** Problem **********************************
	//no compile error if define: Rational operator+(Rational   r2) {...} 
	//   compile error if define: Rational operator+(Rational & r2) {...} 
	//error C2664: 'Rational::operator +' : cannot convert parameter 1 from 'int' to 'Rational &'
	mc3 = mc1.operator+(intVar1); 


	mc3.display("mc1 + 3");

	cout << "exiting... \n";

	return 0;
}


If I try to define the parameter for the operator+ method for Rational as:

(Rational & r2)

I get a compiler error, unless I declare it as

(const Rational & r2)

I don't understand. The constructor provides a default conversion from a single integer to a Rational (int n=0, int d=1) but why does the compiler not allow me to treat the conversion as type (Rational &), only as (Rational), (const Rational), or (const Rational &) ?
It is because operator + has no business modifying the objects on either side of the + sign.
So it is to keep the overloaded operator from behaving too much differently than the original version? So then I would guess wherever a standard C++ operator do not change its argument(s) the compiler will never allow me to define an overloaded version of that operator that DOES change its arguments?
So it is to keep the overloaded operator from behaving too much differently than the original version?


It has more to do with temp object creation. This isn't related to fact that you're dealing with an operator -- you'd have this same problem with any function call.

You are passing an int to a function which takes a Rational&. In order to have a reference to a Rational, you need a Rational object, so the compiler has to create a temporary one. C++ just does not allow this temporary object to be created in this manner for non const references, presumably because it allows for functions to be misused.

Consider the following:

1
2
3
4
5
6
7
8
9
10
void ChangeAString( string& str )
{
  str = "foo";
}

int main()
{
  ChangeAString( "bar" );
  return 0;
}


Logically speaking, the above function call to ChangeAString makes no sense, although if C++ allowed a temp object to be created, the above code would compile without error! Therefore temp objecst are only created if an object is being copied (ie: passed by value) or if the reference is const.

You could side-step this in your code by creating your own Rational object:

1
2
3
4
Rational temp(intVar1);
mc3 = mc1.operator+(temp);   // side-steps the error
       // although this is a very strange way to use the + operator...
       //  why not "mc3 = mc1 + temp;" ? 


While that will compile, it's somewhat silly. It's best to just make your functions const correct.


the compiler will never allow me to define an overloaded version of that operator that DOES change its arguments?


This is allowed. You just can't change the temp object.

However it's not advised ... at least not for most things. One common example of where it is done is with the >> operator with istreams.
Last edited on
Thanks Duoas and thanks Disch.

BTW Disch the reason I was using mc1.operator+ ... instead of mc1 + ... was the compiler error is more specific for the first case.

Disch I tried using a non operator function like you said and your prediction is true, it still fails, so like you said it is not specific to operator+. I tried the temp object Rational temp and that worked, in fact even though mc1.operator+(intVar1) does not work, yet mc1.operator+(Rational(intVar1)) also works, so if I specify the conversion explicitly it can return Rational& but if I let the compiler supply the conversion (which I thought would be the exact same thing Rational(intVar1) !!!) somehow what gets returned cannot be converted to Rational&. Must be something to do with default conversion vs. explicit conversion.

Well too much time spent on this already, thanks again Disch and Duoas. Your input is much appreciated.
Last edited on
Topic archived. No new replies allowed.