Add generic `operator<<` overload for `basic_ostream`

128 views
Skip to first unread message

Lingxi Li

unread,
Jan 22, 2016, 9:37:15 AM1/22/16
to ISO C++ Standard - Future Proposals
When some type T has overloaded `to_string()`, it is reasonable to also overload
`operator<<()`, so you can do `cout << x` instead of `cout << to_string(x)`. It seems
rather verbose and redundant to overload `operator<<()` for each such type. The
standard library should do something so that once `to_string()` is overloaded, we
can get `cout << x` support for free. Specifically, I propose to add the following two
overloads to the standard library.

template <class CharT, class Traits, class T>
typename enable_if<is_same<CharT, char>::value,
                   basic_ostream
<CharT, Traits>&>::type
operator<<(basic_ostream<CharT, Traits>& os, const T& x) {
 
return os << to_string(x);
}

template <class CharT, class Traits, class T>
typename enable_if<is_same<CharT, wchar_t>::value,
                   basic_ostream
<CharT, Traits>&>::type
operator<<(basic_ostream<CharT, Traits>& os, const T& x) {
 
return os << to_wstring(x);
}

ADL will pull in the namespace of `T`. The two overloads are least specialized and
serve as a fallback when `os << x` will otherwise fail to compile.

Patrice Roy

unread,
Jan 22, 2016, 9:54:19 PM1/22/16
to std-pr...@isocpp.org
Out of curosity: wouldn't this be a speed pessimization in a significant amount of use-cases? I know I've never done such things in actual C++ production code (costs way too much), but I might have a skewed viewpoint...

Cheers!

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-proposals/.

Nevin Liber

unread,
Jan 22, 2016, 10:01:48 PM1/22/16
to std-pr...@isocpp.org
On 22 January 2016 at 20:54, Patrice Roy <patr...@gmail.com> wrote:
Out of curosity: wouldn't this be a speed pessimization in a significant amount of use-cases? I know I've never done such things in actual C++ production code (costs way too much), but I might have a skewed viewpoint...

It also introduces an exception-throwing failure mode, and turns a SFINAE-detectable condition (no ostream inserter defined for a given type) into a hard compile error.
--
 Nevin ":-)" Liber  <mailto:ne...@cplusplusguy.com+1-847-691-1404

Lingxi Li

unread,
Jan 23, 2016, 1:39:57 AM1/23/16
to ISO C++ Standard - Future Proposals, ne...@cplusplusguy.com
and turns a SFINAE-detectable condition (no ostream inserter defined for a given type) into a hard compile error

It can be fixed as follows. Enable the overload only if `to_string(x)` is well-formed.

template <class CharT, class Traits, class T,
         
class = typename enable_if<is_same<CharT, char>::value>::type,
         
class = typename enable_if<
                   
true, decltype(to_string(declval<const T&>()))>::type>
std
::basic_ostream<CharT, Traits>&

Zhihao Yuan

unread,
Jan 23, 2016, 2:57:12 AM1/23/16
to std-pr...@isocpp.org
On Sat, Jan 23, 2016 at 12:39 AM, Lingxi Li <lilin...@gmail.com> wrote:
>> and turns a SFINAE-detectable condition (no ostream inserter defined for a
>> given type) into a hard compile error
>
> It can be fixed as follows. Enable the overload only if `to_string(x)` is
> well-formed.

And you also need to follow the FormattedOutputFunction procedure:

http://en.cppreference.com/w/cpp/concept/FormattedOutputFunction

But these are details. The first question should be: How to choose the
customization point(s)? Note that there is another paper trying to
provide to_string based on operator<<,

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0117r0.html

, apparently this questions has to be answered first. However, since
operator<< is already a customization point so you are expected to
see classes providing operator<< without to_string rather than the other
way, so at minimal you need to convince people about why to_string
is a better choice.

(Being said that, I'm not supporting defaulting anyone based on the other).

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
___________________________________________________
4BSD -- http://bit.ly/blog4bsd

T. C.

unread,
Jan 23, 2016, 3:05:49 AM1/23/16
to ISO C++ Standard - Future Proposals
This is a breaking change. Consider: 

enum C : int { X = 16 } ;
std
::cout << std::hex << X;

This now goes to operator<<(int), and prints '10'.

Under your proposal, it would print '16' (and is potentially-throwing).

Lingxi Li

unread,
Jan 23, 2016, 3:45:00 AM1/23/16
to ISO C++ Standard - Future Proposals
Under your proposal, it would print '16' (and is potentially-throwing)

Why is this? I think the original overload will still be chosen, for it is more specialized than the fallback version on the right-hand-side operand.

Lingxi Li

unread,
Jan 23, 2016, 3:55:33 AM1/23/16
to ISO C++ Standard - Future Proposals
And you also need to follow the FormattedOutputFunction procedure: 

 The fallback overload is least specialized. It only kicks in when the program will
otherwise fail to compile.

so at minimal you need to convince people about why to_string is a better choice

I don't think so. People are free to choose whatever they want. I'm just trying to
provide a default `operator <<` implementation if people have only overloaded
`to_string()`. Besides, the default implementation can be overridden.

T. C.

unread,
Jan 23, 2016, 4:07:37 AM1/23/16
to ISO C++ Standard - Future Proposals


On Saturday, January 23, 2016 at 3:45:00 AM UTC-5, Lingxi Li wrote:
Under your proposal, it would print '16' (and is potentially-throwing)

Why is this? I think the original overload will still be chosen, for it is more specialized than the fallback version on the right-hand-side operand.

Your "fallback" is an exact match; the original overload requires a promotion. 

"more specialized" aka partial ordering (well, in this case it's actually the template/non-template tiebreaker) only comes into play if the choices are otherwise equally good. Templates tend to produce *very* good matches.

Vicente J. Botet Escriba

unread,
Jan 23, 2016, 8:38:15 AM1/23/16
to std-pr...@isocpp.org
I've found something like this useful while inserting ordinal enums in a stream. These types use the C-string associated to the enumerator's name, not its value. I have not found it useful for other types.

Instead of overloading to_string, I overload to_c_str(), which return const char* and must be constexpr and noexcept. to_c_str is used when the possible results are know at compile time, there is no allocation, the result is a literal.

Note that to_c_str mustn't (cannot?) be defined for int as it is the case for to_string and so the caveats signaled by Tony (T.C.) don't appear in this case.

For example.

enum class E { a, b };

constexpr const char* to_c_str(E e) noexcept;

template <class Traits>
basic_ostream<char, Traits>&>
operator<<(basic_ostream<CharT, Traits>& os, const C& x) {
 
return os << to_c_str(x);
}


Adding a generic overload as you propose, but using for to_c_str instead, could be useful. O
verloading to_c_str for a type would mean then that the author wants to insert it on a stream as a c-string.

I don't know if is worth adding it, as the needed code is minimal and it would work only for ordinal enums.


Vicente


Moritz Klammler

unread,
Jan 23, 2016, 11:59:16 AM1/23/16
to std-pr...@isocpp.org
Lingxi Li <lilin...@gmail.com> writes:

> When some type T has overloaded `to_string()`, it is reasonable to
> also overload `operator<<()`, so you can do `cout << x` instead of
> `cout << to_string(x)`. It seems rather verbose and redundant to
> overload `operator<<()` for each such type. The standard library
> should do something so that once `to_string()` is overloaded, we can
> get `cout << x` support for free. Specifically, I propose to add the
> following two overloads to the standard library.

This question has been discussed on Stack Overflow before. (I assume
you are the author of that post.)

https://stackoverflow.com/questions/34940754/fallback-to-to-string-when-operator-fails

As I've mentioned in my answer there, `to_string` is just one of many
possible names. It is true that the standard library defines such
functions for integral types (but fore those, `operator<<` is already
overloaded anyway and ADL doesn't work for them) but then again, other
types from the standard library like string streams rather have a `str`
member function. `std::exception` has a `what` member function. Other
people might use even other spellings. If a type has both, an
ADL-findable `to_string` overload and a `str` member function, which one
do you want? Who is going to decide what names can reasonably be
expected to mean "this converts the type into a string representation"?

I believe that such a generic adapter can be useful for some code bases
where you can enable it explicitly. But the standard library should
stay away from adding a possibly confusing feature. I think that the
"official" guideline should remain: If you want your type to be
streamable, overload `operator<<` for it.

Finally, I think that it could often be easier to go the other way round
and implement `to_string` via `operator<<` (aka lexical cast).

Lingxi Li

unread,
Jan 24, 2016, 6:44:52 AM1/24/16
to ISO C++ Standard - Future Proposals
I see. Your arguments are sound and reasonable. Such a facility doesn't seem
to fit into the standard library.
Reply all
Reply to author
Forward
0 new messages