10

I would like to use std::stringstream to create formatted strings, but use the class inline so I don't have stringstream local variables flying around. What I mean is this:

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

int main(int argc, char* argv[])
{
    std::string test = ((std::ostringstream&)
         (std::ostringstream("") << "This is a test: " << 50.1 << "abc")
         ).str();
    std::cout << test << std::endl;
    return 0;
}

This compiles fine in GCC, however the output is the following:

"0x401d0a50.1abc"

So it seems that stringstream treats the first string as a pointer and outputs the address. Subsequent operator<<'s work fine.

How do I fix this? Thanks!

cbuchart
  • 10,847
  • 9
  • 53
  • 93
ElOmmy
  • 203
  • 2
  • 5
  • 4
    [Cannot reproduce](http://coliru.stacked-crooked.com/a/75a72343fac11175) – Baum mit Augen Jun 30 '14 at 16:34
  • This sounds like a missing include somewhere. Are you sure you have included both `` and ``? – James Kanze Jun 30 '14 at 16:36
  • Hi, I've just updated the code to show the complete file. This should compile and returns the output I've described above. Namely it seems to treat the first string as a pointer. – ElOmmy Jun 30 '14 at 16:40
  • @BaummitAugen As per my corrected answer: what he is observing is actually the required behavior, according to the standard, and if you can't observe it, your compiler is broken (or seriously out of date---the critical rule was introduced into C++ around 1990). – James Kanze Jun 30 '14 at 16:47
  • 1
    @BaummitAugen [Reproduced](http://coliru.stacked-crooked.com/a/5a2ca1cc43cdf4fa) without `-std=c++11`. – T.C. Jun 30 '14 at 16:48
  • @JamesKanze I used gcc4.9, it is not that old. – Baum mit Augen Jun 30 '14 at 16:54
  • So this rule changed with c++11? – ElOmmy Jun 30 '14 at 16:56
  • @BaummitAugen I used g++ 4.8, and got the same behavior he did. Adding `-std=c++11` seems to change it, so maybe they changed something in C++11 to make it work. – James Kanze Jun 30 '14 at 16:56
  • @ElOmmy Probably something related to rvalue references. (I hope not: although it would be convenient in this case, in generally, the rule forbidding initializing references with a temporary was introduced because not doing so was found to be too error prone.) – James Kanze Jun 30 '14 at 16:59
  • Hmm so I just tested this in Microsoft VC++ 2013. It also produces the correct output. This is a C++11 compiler as well. So I would suspect there's a change in the standard? – ElOmmy Jun 30 '14 at 17:05
  • @ElOmmy Not so sure about Microsoft. The last time I checked, they still allowed initializing a non-const reference with a temporary. Non-conformant, but that's not the sort of thing which bothers Microsoft too much. – James Kanze Jun 30 '14 at 17:06
  • Could anyone try clang? – James Kanze Jun 30 '14 at 17:07
  • [Clang does the same thing as gcc](http://coliru.stacked-crooked.com/a/91226831b0078351). – T.C. Jun 30 '14 at 17:09

2 Answers2

8

The reason is that the << operator is a member for void const*, but a free function taking an std::ostream& as the left hand argument for char const*. Your std::ostringstream( "" ) is a temporary: you can call member functions (even non-const member functions) on it, but a temporary cannot be used to initialize the non-const reference of the global function.

EDIT:

Two points: first, as has been pointed out, g++ does do what you want if you specify -std=c++11. As T.C. points out, this is specified in §27.7.3.9, which provides a template overload for all << with an rvalue reference for the std::istream parameter. And second, the classic work around is to start the expression std::ostringstream( "" ).flush() <<.... flush is a member function (and so can be called on a temporary) which returns an std::ostream& (so everything else chains nicely); it also does nothing on a std::ostringstream.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Hi James, thanks for the reply. I've updated the code above to show the full file with includes. So I include , , , and . Tried including as well, but this did not change anything unfortunately. The missing include makes sense, but which stl header would be missing? – ElOmmy Jun 30 '14 at 16:44
  • @ElOmmy And I've updated my answer to give the correct reason:-). Sorry that my initial comments were misleading. – James Kanze Jun 30 '14 at 16:46
  • Also, if this is of interest, this seems to be a way around this problem: use *std::auto_ptr(new std::ostringstream("")) instead of std::ostringstream(""). Not pretty, but doesn't matter since I'm wrapping this in a macro anyways. – ElOmmy Jun 30 '14 at 16:52
  • @ElOmmy There are simpler solutions:-). (And of course, you'd need `*std::auto_ptr(...)` in order to get the reference rather than something pointer-like.) – James Kanze Jun 30 '14 at 17:05
2

Found it. The C++11 standard special-cases rvalue streams with an extra template operator<< overload. §27.7.3.9 of the standard:

Rvalue stream insertion [ostream.rvalue]

template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);

Effects: os << x

Returns: os

It's obviously a better match than the member operator<< that takes a const void * and hence is selected by overload resolution in C++11 mode. In C++98 mode this overload doesn't exist (since there are no rvalue references) and the only viable overload is the member operator<< (since, as James Kanze explains in his answer, the temporary can't bind to the non-const lvalue reference in the free operator<< overload that takes a const char *).

Community
  • 1
  • 1
T.C.
  • 133,968
  • 17
  • 288
  • 421