The file streams and string streams do not define stream insertion/extraction operators of their own;
these are either inherited from the base class(es)
or free functions written in terms of std::basic_ostream<>/std::basic_istream<>
It has to do with inheritance. The << operator is the same one as used in any other type of output stream, like cout, or stringstream. Internally the << operator just performs the conversion from the given type into an array of characters, what happens with this array of characters depends on the exact type of your stream.
Basically it allows you to use your overloaded operator for any outputstream type you come across. Say you define your function like this:
ostream& operator<<(ostream&, const Obj&);
You could use your new overloaded operator on file streams, string streams, cout or whatever custom stream you come across. ostream is just a typedef for a basic_ostream<char>.
In my project I wrote the operator<< to accept a fstream because, for now, I want it to work only if a file is passed.
Say I want different behaviours with files, cout and other streams...
The fact that file << "Obj:"; returns a basic_ostream is confusing. I preferred it to return a basic_fstream (like my function does, to enable chaining!)
Since basic_ostream is also the one defining all other versions of operator<<, this still allows for chaining, for as long as you make sure all your operators accept the same type as all other overloads of operator<<. It also allows the code in the standard library to only be defined once, instead of once per type of stream.
The only problem is that you can't differentiate between as easily, although using dynamic_cast you can still check if your instance of the stream is actually a file stream, so this is still possible, but it requires a bit of a workaround.
> Say I want different behaviours with files, cout and other streams...
For that, you would need to deal with the stream buffer classes directly.
If your overloaded operator gets a plain vanilla std::ostream, or for that matter you write to std::cout,
there is no guarantee that it is not writing to a file. Try out this program:
So if I understood correctly, every stream's operator<< is defined to take and return the base class ostream... and I'm kinda forced to do the same If I want my code to be compliant with all other types
Sometimes I think the implementation won't change... but if I need something that behaves differently depending on the stream type, then I'll follow your advices.