38

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?

10
  • 1
    Maybe because of the character type template parameter? This function can have the same ("efficient") implementation for any character type by using the locale's widen. Commented Jul 7, 2015 at 17:07
  • 1
    Interesting topic. Might be a reason why what was discussed here doesn't work. Commented Jul 7, 2015 at 17:14
  • 1
    @dyp, that does not seem very convincing. Commented Jul 7, 2015 at 17:14
  • 3
    Well, right now for all streams we have an operator<< taking a charT and an operator<< taking a char. That's going to be tricky to do as members since charT can - and often is - char. Commented Jul 7, 2015 at 17:16
  • 3
    open-std.org/JTC1/SC22/WG21/docs/papers/1996/N0918.pdf Commented Jul 7, 2015 at 17:51

2 Answers 2

13
+100

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.

Sign up to request clarification or add additional context in comments.

4 Comments

I'm not sure I fully understand your answer: There's a generic template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, char c); function template that works fine for wchar_t.
@ dyp: Yes, but at the same time they felt the need to introduce the dedicated specialization for inserting char values into char streams. I don't really know why, but the library spec says such specialization exists.
I'm not entirely sure if that "specialization" prevents an overload resolution issue.
@AnT, Apparently, library authors wanted to prevent this from happening. is most likely the reason. It will be interesting if someone involved in the development of the libraries can throw some light on the subject.
6

One reason is following the general C++ advice of preferring non-member non-friend functions to member functions. This is item 23 in Scott Meyer's Effective C++. This is discussed in stackoverflow.

2 Comments

Good to mention this. This rule-of-thumb does not seem to be known by many programmers, leading to god object or object orgy antipatterns...
Right, I usually think about it in terms of coupling or with avoiding that everything that one could do with some class becoming overly coupled. I'd rather that enhancements by done via the public interface so I don't have to worry about how so many function could potentially change state the object (affecting class invariants), even if const member can help as well. This also makes it easier to partition functions into various more focused headers files.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.