1
#include <iostream>
#include <fstream>
using namespace std;

class binaryOperators 
{
    public:
        int i;

        binaryOperators (int tempI = 0)
        {
            i = tempI;
        }

        binaryOperators operator<< (const binaryOperators &right);
};

binaryOperators operator<< (const binaryOperators &left, const binaryOperators &right)
{
    cout << "\nOne";
    return left;
}

binaryOperators binaryOperators :: operator<< (const binaryOperators &right)
{
    cout << "\nTwo";
    return *this;
}

int main ()
{   
    binaryOperators obj;

    // Compiler's behavior: This statement calls the overloaded operator << declared inside the class.
    obj << 5 << 3 << 2;
    // Compiler's behavior: This statement calls the overloaded operator << declared outside the class.
    2 << obj;

    return 0;
}

I have written the comments inside the main() function.
What's the reason for this sort of compiler's behavior?

Is this behavior compiler dependent?

GCC on Linux

Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411
  • 1
    you should really try to write better variable names. `l` looks very much like `1`. – default Jan 03 '12 at 10:08
  • @Default sorry, I'll take care next time. – Aquarius_Girl Jan 03 '12 at 10:20
  • Please put new answers in new posts, otherwise I won't get notifications. – Aquarius_Girl Jan 04 '12 at 09:12
  • 1
    @Anisha Your Edit 1 question is unrelated to the original question, so it should be posted seperately. Without testing it, I believe the problem is return (*this + right.i) in the first (member) operator implementation. I think you meant (this->i + right.i). – zennehoy Jan 04 '12 at 09:42
  • @zennehoy that worked!! Thanks. But what was wrong in *this? Should I create a new question or you'll answer it here? – Aquarius_Girl Jan 04 '12 at 09:44
  • @Anisha By using *this you have an addition of type binaryOperators + int. This will cause the operator+ to be called again, resulting in infinite recursion. If that's unclear, start a new question and I'll answer in more detail there (though I'm sure something similar has been asked before). – zennehoy Jan 04 '12 at 09:47
  • @zennehoy It is here: http://stackoverflow.com/questions/8724845/why-does-returning-this-result-in-an-infinite-loop – Aquarius_Girl Jan 04 '12 at 09:51

2 Answers2

2

The behavior you're seeing is caused by const-correctness. The operator<< defined within the class is non-const, so it can only operate on a non-const object or reference, such as obj. The non-member version outside the class has two constant operands.

If you wrote the member version as a non-member, it would look like this:

binaryOperators operator<< (binaryOperators &left, const binaryOperators &right)
{
    cout << "\nTwo";
    return left;
}

When overload-matching, the compiler chooses the best fit. In the first case, the left operand is non-const, so it chooses the member operator. In the second case, the left operand is an rvalue (temporary binaryOperators), which is referenced as const, so the non-member operator is chosen.

zennehoy
  • 6,405
  • 28
  • 55
  • Were you meaning to return a reference? – Simon Richter Jan 03 '12 at 10:02
  • I based this on the operator implementation provided by the OP. Admittedly returning a reference would be more in line with what a user might expect, but then again the functionality is missing anyways. – zennehoy Jan 03 '12 at 10:06
  • If you return a value here, the next `<<` cannot use the member operator as you expect a non-`const` reference as the LHS. That's my point. :) – Simon Richter Jan 03 '12 at 10:22
  • isn't the main reason that 2 is not of type binaryOperators? In the first case, obj is of type binaryOperators and the numbers are implicitly convertible to that. In the second case, 2 is not of type binaryOperators thus it can't call the member. It then searches for possible "free functions" that match. In this case there is such an operator. I don't think that C++ implicitly converts objects to find possible candidates for member functions to call. – Tobias Langner Jan 03 '12 at 10:29
  • zennehoy, great answer. What is the way to make the left operand const in the current case? – Aquarius_Girl Jan 03 '12 at 10:36
  • 1
    Declare the member function as const: `binaryOperators operator<< (const binaryOperators &right) const`. This will cause an ambiguity between the member and non-member operators though. In general, it is better to use the non-member version as Tobias pointed out in Simon's answer (to make better use of implicit conversions). – zennehoy Jan 03 '12 at 10:49
2

This behaviour makes complete sense:

  • When a member function exists and matches, then it is a good idea to use it in preference to a free function, otherwise code outside the class could inadvertently break up the class encapsulation (for example, if another member function were to use the operator<<).

  • Automatic conversion works by creating a list of candidate functions, then attempting to find conversions for any parameters that require it. In order to find the member function, a conversion would have to happen before building the candidate list, so only the free function is found.

Simon Richter
  • 28,572
  • 1
  • 42
  • 64
  • that's why Scott Meyer advices to never create operators as member functions. You should always create them as free functions to benefit from implicit conversion for your types. – Tobias Langner Jan 03 '12 at 10:31
  • @TobiasLangner by using `friend`? @Simon, thanks for the answer. – Aquarius_Girl Jan 03 '12 at 10:37
  • 1
    Use friend only if you absolutely need direct access to private/protected members. To maximize encapsulation, operators should be non-member non-friends, see also http://www.gotw.ca/gotw/084.htm . – zennehoy Jan 03 '12 at 10:54
  • @zennehoy Thanks that makes sense. also, if you don't write @ Anisha I won't get the notifications. – Aquarius_Girl Jan 03 '12 at 11:04