Why the typical header of stream manipulation with a user-defined class C is typically like this:
std::ostream& operator<<(std::ostream& os, const C& c);
std::istream& operator>>(std::istream& is, C&);
and not like this:
template <class CharT, class Traits>
std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& os
const C& c);
template <class CharT, class Traits>
std::basic_istream<CharT, Traits>& operator>>(
std::basic_istream<CharT, Traits>& is
C& c);
My question is why the usual overloading of stream operators is done with std::ostream, which is a typedef for char of std::basic_ostream, and why it is not done directly with std::basic_ostream ?
For example:
class C
{
...
};
std::ostream& operator<<(std::ostream& os, const C& c)
{
...
}
int main()
{
C c;
std::wofstream myFile("myFile.txt");
myFile << c; //Impossible
}
The operator<< written here limit us to use only stream object that are specialized for char (std::ostream, std::ostringstream, ...).
So if using std::ostream is more limiting than std::basic_ostream,
why std::basic_ostream is never mentioned when talking about stream operators overloading?
In practice, there are two different character types used:
wchar_tas a result of promises made by the Unicode people a long time ago (that Unicode would only use 16 bits and that each character would consist of just one unit) and which have been broken since.charwhich is now mostly considered to be a byte in a UTF-8 encoding (obviously, not universally).In retrospect, the introduction of
wchar_t(and even more so ofchar16_tandchar32_t) was ill-advised and the world would be better off if onlycharwould be used. As a result, those not bothered by Windows don't care about awchar_tversion of the I/O operations and Windows in general seems to punt on IOStreams in general (the MSVC++ implementation is known to be slow and there is zero intention to do anything about it).Another reason is that writing templatized I/O operators is seen to be adding complexity to an already complex system. Few people seem to understand IOStreams and among these few even fewer are interested in supporting multiple character types.
One aspect of the perceived complexity with templatizing I/O operators is the assumption that the implementation needs to go into the header which is, of course, not true given that there are essentially just two character types IOStreams are instantiated with (
charandwchar_t): although IOStreams can be instantiated with other character types, I'm pretty sure that hardly anybody actually does so. Although I know what it takes, it would probably still take me at least a day to define all the necessary facets. Thus, the template definitions could be defined in suitable translation units and instantiated there. Alternatively, instead of defining the operators are templates they could be fully specialized.Independent on how the templated operators are defined, it is generally more work. If done naively (i.e., directly using e.g.,
std::ctype<cT>) the result will be slow and when doing it properly (i.e., caching results fromstd::ctype<cT>) it will be quite involved.Taking it all together: why bother?
If I had to write to
std::wostreamor read fromstd::wistreamI'd actually create a filtering stream buffer which just translates the the characters written/read using a suitablestd::codecvt<...>facet (or even just usingstd::ctype<wchar_t>'swiden()ornarrow()). It wouldn't deal with proper internationalization of strings but thestd::localefacilities aren't really up to proper internationalization anyway (you'd need something like ICU for that).