After looking at the ostream::operator <<
c++ reference,
I noticed the following declarations:
ostream& operator<< (bool val);
ostream& operator<< (short val);
ostream& operator<< (unsigned short val);
ostream& operator<< (int val);
ostream& operator<< (unsigned int val);
ostream& operator<< (long val);
ostream& operator<< (unsigned long val);
ostream& operator<< (float val);
ostream& operator<< (double val);
ostream& operator<< (long double val);
ostream& operator<< (void* val);
ostream& operator<< (streambuf* sb );
ostream& operator<< (ostream& (*pf)(ostream&));
ostream& operator<< (ios& (*pf)(ios&));
ostream& operator<< (ios_base& (*pf)(ios_base&));
But then I found out that there are also the following declarations:
ostream& operator<< (ostream& os, char c);
ostream& operator<< (ostream& os, signed char c);
ostream& operator<< (ostream& os, unsigned char c);
ostream& operator<< (ostream& os, const char* s);
ostream& operator<< (ostream& os, const signed char* s);
ostream& operator<< (ostream& os, const unsigned char* s);
Why are the char/string output operators not member functions?
The first group of operators are members of the stream class.
Most operator overloads, like those in the second group, are not.
As to the why, it is likely just a historical accident. The operators for built in types can be added to the stream classes, and obviously they were (long before C++ was standardized). The standard just documents existing practice here.
Operators for user defined types obviously cannot be added to the stream classes, so they are implemented as free functions.
In retrospect it would have been more consistent to make all the operators free functions, but that would possibly break some old programs.
The others have described the differences, here's my shot at why they are split.
The non-member versions, which take char
variants as parameters are specialized depending on type of the target stream's base character type and handled depending on locale, because standard defines specific behavior (o << '1'
has different behavior than oo << 33
, it has to be handled properly if o
is basic_ostream<wchar_t>
, etc).
E.g. char
writen to basic_ostream<wchar_t>
is widened, while if written to basic_ostream<char>
it is not (27.6.2.5.4). So actually there are multiple overloads of operator<<
, e.g:
basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, char _Ch)
basic_ostream<char, _Traits>& <<(basic_ostream<char, _Traits>& _Ostr, char _Ch)
If they were defined as member functions, you wouldn't be able to specialize them like that as you would have to partially specialize the whole class, not just a single member, since standard doesn't allow partial specialization of member functions.
There are actually two families of overloader operator<<
s, as you have found out.
One family is the overloaded member ostream::operator<<
(which gets the ostream
reference implicitly as the this
pointer) and the other family is the overloaded free function operator<<
which gets the ostream
reference explicitly as an argument.
When you want to add streaming capability to a class of your own, you add a free function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With