# A version of operator<< that returns ostringstream instead of ostream

I always thought it would be handy to be able to write:

const std::string s = (std::ostringstream() << "hi" << 0).str();


This doesn't work in C++ by default because operator<< returns the ostringstream as a ostream, which obviously doesn't have the str() member.

I was pleasantly surprised though to discover that this can be done in C++11:

#include <iostream>
#include <sstream>
#include <string>

template <typename CharT, typename traits, typename T>
std::basic_ostringstream<CharT,traits>&& operator<<(std::basic_ostringstream<CharT,traits>&& out, const T& t) {
static_cast<std::basic_ostream<CharT,traits>&>(out) << t;
return std::move(out);
}

int main() {
const std::string s = (std::ostringstream() << "hi" << 0).str();
std::cout << s << std::endl;
}


It needs rvalue references and move semantics to bind the temporary to a reference here.

1. Is there a good reason why this isn't provided by the standard library by default? Is it an oversight? Making this idiom legal code seems like it might offer a number of benefits.
2. In my local C++11 project, I provide some "utility" code. Is there any reason why I shouldn't include this and use it widely?

How about the more generic version:

template <typename S, typename T, class = typename
std::enable_if<std::is_base_of<std::basic_ostream<typename S::char_type,typename S::traits_type>, S>::value>::type>
S&& operator<<(S&& out, const T& t) {
static_cast<std::basic_ostream<typename S::char_type,typename S::traits_type>&>(out) << t;
return std::move(out);
}

-
You might be interested in this gizmo I had reviewed when the site first came live: codereview.stackexchange.com/questions/226/… –  John Dibling Nov 23 '11 at 20:44

Bjarne gives a pretty succinct description of why this (and many similar) features aren't in the standard. He points out that virtually everybody who talks to him about C++ has essentially the same thing to say: "C++ is way to big and way too complex. You should really work at making it a lot smaller and simpler. Oh, but while you're at it, you should add this one really great feature I thought of"

It kind of reminds me of things I've seen about how questions of political polls can be/ are phrased. If I ask: "Do you think this country should do more to [increase jobs | help the needy | etc.]?" I can count on a large majority of people saying "yes." If I ask "Do you think we should raise taxes by X%?", I can count on an equally large majority saying "No". I can give nearly a guaranteed outcome from my poll just by choosing whether to ask about the hoped-for outcome or the cost.

Bottom line: Each feature you add has a cost, and focusing on the outcome doesn't remove the cost. To design a usable language/library, you need to restrict features to the few that provide the greatest positive outcome for the lowest cost. In the case of the C++ committee, they've been pretty clear about a few things, and one is that most new features will barely be considered at all unless they enable fairly substantial improvements in code that uses the new feature and have extremely minimal cost, especially in terms of breaking any existing code.

-
That makes a lot of sense. When I asked I wasn't quite sure if there was some terrible glaring problem that it accidentally introduces that made it a bad idea or if it's just not important enough to be worth the complexity of adding. From the answers here it looks like it's the latter. –  Flexo Nov 16 '11 at 16:50
I agree. What is nice about C++ is that you can define these yourself for your code when you need them, even though there is not enough of a need to justify putting it in the standard. –  hosch250 Dec 29 '13 at 21:51

Its a nice trick. But I don't see what it buys you.

So you have a std::string object that you now use.

int main() {
const std::string s = (std::ostringstream() << "hi" << 0).str();
std::cout << s << std::endl;
}


In C++03 I would have a std::stringstream object that I now use (which can then be used in the same place as the std::string object).

int main()
{
std::ostringstream message;
message << "hi" << 0;       // one extra line.
std::cout << message.str() << std::endl;
}


I don't see the advantage. Maybe if you can show the code being used in a larger context where it provides an advantage over std::stringstream.

-
The advantage is atomicity. Single calls to operator<< on std::cout and std::cerr are guaranteed to be atomic. You can get interleaving with output from other threads with std::cout << 'a' << 1 << false;, but not with std::cout << static_cast<const stringstream&>(std::stringstream() << 'a' << 1 << false).str();. –  uckelman Apr 4 at 9:01
@uckelman: Yes. That is NOT what I am debating. If you see my code that part has not changed. My argument is that adding an extra list to make an explicit object (rather than a temporary) is more readable and only adds a single line. –  Loki Astari Apr 4 at 14:15

Is there a good reason why this isn't provided by the standard library by default? Is it an oversight? Making this idiom legal code seems like it might offer a number of benefits.

Other than that the standard was already ~5 years delayed, and this little trick ultimately doesn't buy you much?

There are millions of cute little party tricks and convenience functions that can be written in C++. For one of them to get standardized, they have to offer a pretty significant benefit.

I see no harm in it though, and no reason why you shouldn't use it. But I don't think it's significant enough to warrant inclusion in the standard.

-

const std::string s = static_cast<std::ostringstream&>(

Shove that in a #define and you're golden...
... except not really. Any operator<< implementations that are free functions rather than member functions will fail, because their LHS operand must be std::ostringstream& and, as a ref-to-non-const, this will not bind to the temporary std::ostringstream().
The almost-amusing result of the above example is that s will contain text like "0x804947c0", but swapping "hi" and 0 will result in the behaviour you'd expect ("0hi").