3

Consider the following code:

 class A { public: A& operator=( const A& ); const A& operator+( const A& ); const A& operator+( int m ); }; int main() { A a; a = ( a + a ) + 5; // error: binary '+' : no operator found which takes a left-hand operand of type 'const A' } 

Can anyone explain why the above is returned as an error?

"( a + a )" calls "const A& operator+( const A& )" and returns a constant reference which is then passed to "const A& operator+( int m )" if I'm not mistaken.

How can one fix the above error (without creating a global binary operator+ or a constructor that accepts an int) such that the statement inside main() is allowed?

1
  • Thanks for all the explanations. I know above is not the best way to implement the operators but I was just wondering why the compiler is complaining in the above code. Commented Mar 7, 2010 at 8:56

6 Answers 6

7

which is then passed to "const A& operator+( int m )" if I'm not mistaken

No. Since the LHS is a const A& and RHS is an int, it will call*

[anyType] operator+ (int rhs) const // ^^^^^ note the const here. 

as you've only provided the non-const version const A& operator+( int m ), the compiler will complain.

*: Or operator+(const int& rhs) const or operator+(float rhs) const... The crucial point is that it must be a const method.

Sign up to request clarification or add additional context in comments.

Comments

6

operator+ should return an instance, not a reference:

// as member function A operator+(const A& other); // as free function A operator+(const A& left, const A& right); 

To explain the specific problem is "returns a constant reference which is then passed to const A& operator+( int m )". Since you have a const reference, it cannot call that function because it's not a const method (i.e. const A& operator+( int m ) const).

That said, that is not the way to fix operator+. If you're returning a reference, what is it a reference to? A local in operator+ would be bad as you shouldn't return a reference to a local. A reference to a global would be bad because it will limit how your code can be used properly. A reference to allocated memory would be bad because it will leak memory. A reference to *this would be bad because then operator+ is acting like operator +=.

2 Comments

@R Samuel: Yes, I understand. I took this out from an exam that's why I didn't change it.
@jasonline: If you really took this crap out of an exam, then, if you can, ditch that exam. That code is about as bad as you can do operator+, I see very little room for making it worse.
2

Because you are modifying the left-hand side object when adding. You can't do that with a const object. Take Samuel advice btw, because the idiomatic way is to return a new copy of the added objects.

3 Comments

@AraK: Yes, I understand it should return a new object. That code was taken from an exam so I didn't bother to change it.
@jasonline: Are you sure the exam didn't implement += this way? It's very hasd to imagine that any exam would get + that wrong.
@sbi: I guess the exam was just trying to test me if I could identify which part is causing the error. Not really trying to implement it the correct way.
1

The function needs to be const:

const A& operator+( int m ) const;

Comments

1

As const A& operator+( const A& ) returns a const reference a non-const member function const A& operator+( int m ) can not be called over a const object. Either, the first operator should be defined as A& operator+( const A& ) or, the second operator as const A& operator+( int m )const; However, these changes will only make them technically correct, not aesthetically in majority of the cases, as a binary operator is not supposed to modify any of the input argument and yet to compute a result and return. Thus the result have to be returned by value or in case of C++0x , as a r-value reference. i.e A operator+(const A& rhs)const or A&& operator+(const A& rhs)const;

Comments

1

The problem is that (a+a) returns a so-called rvalue (basically a fancy term for a temporary). While you can invoke member functions on an rvalue, you can only invoke const member functions. Also, everyone is right in saying that operator+ must alwasys return a new value.

Your operators should be implemented like this:

A operator+( const A& ) const; A operator+( int m ) const; 

However, binary operators which don't modify their left argument are probably better implemented as free functions:

class A { ... }; A operator+(const A& lhs, const A& rhs); A operator+(const A& lhs, int rhs); 

Usually, they are implemented on top of operator+=, which is implemented as a member:

class A { public: A& operator+=(const A& rhs); A& operator+=(int rhs); }; inline A operator+(A lhs, const A& rhs) // note: lhs is passed by copy now { lhs += rhs; return lhs; } A operator+(A lhs, int rhs) // note: lhs is passed by copy now { lhs += rhs; return lhs; } 

5 Comments

@sbi: Thanks for the info, yes I know the best way should be to implement is as a global and add a constructor taking an int for the conversion. I just didn't understand the const part that's why.
@jasonline: The best way is usually not to use an implicit cast constructor, because that would require the creation of a temporary A from an int. Usually, the best way is to define distinct operator overloads for the int which avoid the creation of temporary. (If the constructor is trivial and all the code involved is inline, the compiler might be able to eliminate the temporary.)
You mean define two overloaded operator where the int is on the lhs and the other where the int is on the rhs? But what's so bad about the creation of the temporary in this case anyway?
@jasonline: Yes, that's what I mean. With your A, temporary or not probably won't make much difference. But think of std::string: If, in order to append a string literal to a string (s1 + "blah") the literal had to be turned into a std::string first (basically, s1 + std::string("blah")), that might cause dynamic memory allocation and deletion and thus a serious run-time cost.
Oh, I just thought it would be more convenient though and less redundant. But if it really causes some serious run-time cost then I guess you have a point. Thanks.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.