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,354
  • 2
  • 17
  • 37
  • 1
    Basically `a << b` can mean `a.operator<<(b)` *or* `operator<<(a, b)`, so when you make a friend function `operator<<(cout, obj)` would be called. See [here](https://stackoverflow.com/a/4421715/5754656) for more information. – Artyer Aug 29 '19 at 00:28
  • @Artyer I see the part that says "A binary infix operator @, applied to the objects x and y, is called either as operator@(x,y) or as x.operator@(y).4" but i don't see how or where it's implied that "a << b can mean a.operator<<(b) or operator<<(a, b)" – csguy Aug 29 '19 at 00:34
  • @Artyer Answer in the answer section mate – Lightness Races in Orbit Aug 29 '19 at 00:43
  • @csguy It's not implied. It's directly stated. Literally by the quote you quoted. – Lightness Races in Orbit Aug 29 '19 at 00:43
  • regarding the edit: you don't see how "`x << y` is called as either `operator<<(x,y)` or [...]" answers your question of how `cout << obj` can find the function `operator<<(ostream&, const Obj&)` ? – M.M Aug 29 '19 at 01:05

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
  • 378,754
  • 76
  • 643
  • 1,055
  • oh i guess i didn't understand it. so when you do cout << obj, it's something like cout << function(ostream, obj); – csguy Aug 29 '19 at 00:49
  • 1
    @csguy `std::cout << obj` calls `operator<<(std::cout, obj)` if there is no `std::ostream::operator<<` member that can take the `obj` as input. `std::cout` is an object instance of type `std::ostream`, so it can be passed as-is to a `std::ostream&` parameter, thus satisfying the call to `std::ostream& operator<<(std::ostream &, const decltype(obj) &)`. – Remy Lebeau Aug 29 '19 at 01:05
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,997
  • 21
  • 20