Why does fstream operator<< return basic_ostream<char>&?

I overloaded my class' operator<< to accept a file stream and a class object

1
2
3
4
5
fstream& operator<<(fstream& file, const Obj& obj)
{
   file << obj.toString();
   return file;
}


Then, when I decided to use my overload:

 
file << "Obj: " << myObj;


A compilation error occurred

binary '<<': no operator found which takes a right-hand operand of type 'const Obj' (or there is no acceptable conversion)


After a bit of debugging I figured out that

 
file << "Obj";


returns a basic_ostream<char>&

and

 
file << myObj;


returns a basic_fstream<char>&


Why? What is happening?

My overload was supposed to return a fstream reference, not a basic_fstream<char>&

And why is file << "Obj:"; returning basic_ostream<char>&?

I wrote my overload to accept a fstream because I was expecting to receive a fstream... you know, to allow chaining

P.S: I know I can solve this problem by modifying my operator<< so that it accepts the base class ostream... but it's just a curiosity
Last edited on
> why is file << "Obj:"; returning basic_ostream<char>&

This is the function that is called:
1
2
template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,  const char* s );

http://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt2

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>.

You can see more on the inheritance in the standard library in the page on ostream on this site:
http://www.cplusplus.com/reference/ostream/ostream/
Last edited on
Thank you for the answers.

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <fstream>

int main()
{
    std::filebuf fbuf_in ;
    fbuf_in.open( __FILE__, std::ios::in ) ;
    const auto old_cin_buf = std::cin.rdbuf( std::addressof(fbuf_in) ) ;

    std::filebuf fbuf_out ;
    fbuf_out.open( "out.cpp", std::ios::out ) ;
    const auto old_cout_buf = std::cout.rdbuf( std::addressof(fbuf_out) ) ;

    char c ;
    // read each character from this file and write it to file "out.cpp"
    while( std::cin.get(c) ) std::cout << c ;

    std::cout.rdbuf(old_cout_buf) ;
    std::cin.rdbuf(old_cin_buf) ;
}
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

Well, ok then :)

Thank you very much!
Not a good idea; but for a user-defined type, this is possible:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <fstream>
#include <sstream>

struct my { int v = 0 ; } ;

std::ostream& operator<< ( std::ostream& stm, my m )
{
    if( dynamic_cast<std::filebuf*>( stm.rdbuf() ) )
        return stm << "write to file: my{" << m.v << '}' ;

    else if( dynamic_cast<std::stringbuf*>( stm.rdbuf() ) )
        return stm << "write to string: my{" << m.v << '}' ;

    else return stm << "write to generic ostream: my{" << m.v << '}' ;
}
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.

Thank you very much :)
Topic archived. No new replies allowed.