0

According to Can't Overload operator<< as member function

When overloaded as a member function, a << b is interpreted as a.operator<<(b), so it only takes one explicit parameter (with this as a hidden parameter).

Since this requires that the overload be part of the class used as the left-hand operand, it's not useful with normal ostreams and such. It would require that your overload be part of the ostream class, not part of your class. Since you're not allowed to modify ostream class, you can't do that. That leaves only the global overload as an alternative.

I know that for example:

friend std::ostream& operator<< (std::ostream &out, const Obj &obj);

acts like a function that takes an object of ostream and some object that you are trying to print and then returns an ostream object.

But I don't understand how doing cout << obj will call this function.

Wouldn't cout << obj do something like cout.operator<<(obj), which is exactly what we don't want? So why does it actually call the function? And why does it allow the return value to go back to cout?

EDIT:

I had read over What are the basic rules and idioms for operator overloading? previously and it states

"A binary infix operator @, applied to the objects x and y, is called either as operator@(x,y) or as x.operator@(y).4"

which provides some further clarity, but I don't see how it answers my question.

csguy
  • 1,263
  • 1
  • 12
  • 32

2 Answers2

2

That's exactly what the answer you quote says, although it doesn't do so very well.

An operator overload can be a member, or a non-member.

For example, anOstream << aT can be resolved with a ostream& ostream::operator<<(T) (or similar) or with a free function ostream& operator<<(ostream&, T). Either of those can be called. That's just how it is. That's what the standard says.

Since we can't add things to ostream, the latter is how we go about it (though, for your own types, it would be pretty much up to you).

Notice how I chose a return type of ostream& for those examples; that's how "the return value goes back to cout": the left-hand operand is simply returned back by reference.

Lightness Races in Orbit
  • 369,052
  • 73
  • 620
  • 1,021
2

The compiler would call cout.operator<<(obj) if it was present but if it isn't it will look for a compatible global function.

e.g. Below it will call the member function. But if that is commented out it will call the global function.

#include <iostream>
class Ostr;

class Obj {
    public:
    void print(Ostr& os) const;
};
class Ostr {
    public:
    Ostr& operator<<(const Obj& obj){
        std::cout << "member";
        obj.print(*this);
        return *this;
    }
    Ostr& operator<<(const char* obj){
        std::cout << obj;
        return *this;
    }
};
void Obj::print(Ostr& os) const {
    os << "Obj";
}

template<class T>
auto operator<<(Ostr& os, const T& t) -> decltype(t.print(os), os) { 
    os << "global";
    t.print(os); 
    return os; 
} 

int main() {
    Obj obj;
    Ostr ostr;
    ostr << obj;
    return 0;
}
QuentinUK
  • 2,842
  • 18
  • 20