Search code examples
c++c++11ostream

Why is operator<< function between std::ostream and char a non-member function?


When I ran the following program

#include <iostream>

int main()
{
   char c = 'a';
   std::cout << c << std::endl;
   std::cout.operator<<(c) << std::endl;

   return 0;
}

I got the output

a
97

Digging further at http://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt, I noticed that std::ostream::operator<<() does not have an overload that has char as the argument type. The function call std::cout.operator<<(a) gets resolved to std::ostream::operator<<(int), which explains the output.

I am assuming that the operator<< function between std::ostream and char is declared elsewhere as:

std::ostream& operator<<(std::ostream& out, char c);

Otherwise, std::cout << a would resolve to std::ostream::operator<<(int).

My question is why is that declared/defined as a non-member function? Are there any known issues that prevent it from being a member function?


Solution

  • The set of inserters for std::basic_ostream includes partial specializations for inserting char, signed char, unsigned char and such into basic_ostream<char, ...> streams. Note that these specializations are made available for basic_ostream<char, ...> streams only, not for basic_ostream<wchar_t, ...> streams or streams based on any other character type.

    If you move these freestanding templates into the main basic_ostream definition, they will become available for all specializations forms of basic_ostream. Apparently, library authors wanted to prevent this from happening.

    I don't really know why they wanted to introduce these specializations on top of the more generic

    template<class charT, class traits>
    basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
                                            char);
    

    inserter, but apparently they had their reasons (optimization?).

    The same situation exists for C-string inserters. In addition to the more generic inserter

    template<class charT, class traits>
    basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
                                            const char*);
    

    the library specification also declares more specific

    template<class traits>
    basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&,
                                           const char*);
    

    and so on.