
Emil Dotchevski wrote:
You can still do this with what I am proposing. When someone does
foo x; std::string s=to_string(x);
this would bind to the foo to_string overload if available, if not it would bind to the foo/ostream overload of << if available, etc. The idea is to automatically pick the best conversion available to produce the string the caller needs.
This makes the to_string library implementation more complex, but the benefit is that it is possible to define simple, non-template to_string overloads for various types. The reason I like that is because I don't have to put to_string implementations in headers.
Also, it makes it very easy to integrate 3rd party types into the framework. Suppose you have something like:
class bar { public: std::string getstr() const; private: ... };
and you want to integrate it into the to_string framework. All you have to do is (presumably you can't alter bar itself):
std::string to_string( bar const & x ) { return x.getstr(); }
(note, no coupling between the above to_string overload and the to_string library) and now you can do:
bar x; std::wstring s=to_wstring(x); //the library handles string->wstring conversion automatically.
And what if I write my own class outer_bar that owns bar and also want to be ostreamable? I would have to duplicate my operator<< in order to call either to_string or to_wstring, wouldn't I? struct outer_bar { bar b_; friend std::ostream& operator<< ( std::ostream& s, outer_bar const& ob) { return s << to_string(ob.b_); } friend std::wostream& operator<< ( std::wostream& s, outer_bar const& ob) { return s << to_wstring(ob.b_); } }; Things will get even worse with char16_t and char32_t. Why not make it a template? It would still cover your case just fine: struct outer_bar { bar b_; template< typename CharT, typename TraitsT > friend std::basic_ostream< CharT, TraitsT >& operator<< ( std::basic_ostream< CharT, TraitsT >& strm, outer_bar const& ob) { typedef std::basic_string< CharT, TraitsT > string_t; return s << convert< string_t >(ob.b_); } }; And supporting bar for the templated "convert" is no less difficult than what you suggested: template< typename StringT > StringT convert(bar const & x); template< > std::string convert(bar const & x) { return x.getstr(); } template< > std::wstring convert(bar const & x) { return convert< std::wstring >(convert< std::string >(x)); } The equivalent of the latter specialization will be needed in your approach, too. If the library is clever enough, it may be done in generic manner in the library itself.