5

I want to read directly into a string with code like this:

std::string myString(( std::ostringstream() << myInt << " "
                                            << myFloat << " "
                                            << std::boolalpha << myBool ).str());

But VS2012 is giving me a complaint that basic_ostream doesn't have an str() method.

Is there a way to do this with an anonymous stringstream?

David G
  • 94,763
  • 41
  • 167
  • 253
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    This code compiles as-is and runs fine on Clang. (Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)) – jbruni Oct 29 '13 at 17:54

3 Answers3

13

The operator<< for streams returns std::ostream & which doesn't have str() member function. You need to use cast.

static_cast<std::ostringstream&>(std::ostringstream() << etc).str()

Differences between C++03 and C++11 when using temporary stream!

C++03

But be aware (in C++03) that std::ostringstream() creates a temporary object, which means the non-member overloads of operator<< cannot be invoked for the first << because all of them accepts the first argument as std::ostream& which cannot bind to the temporary object. The temporary object will be able to invoke only member functions.

That means, the following would give you address instead of string:

static_cast<std::ostringstream&>(std::ostringstream() << "XYZ").str()

Because the overload which takes char const* as argument is a non-member function, which cannnot be invoked, so the above code end up calling the member function which takes void const* as argument and thus "XYZ" is implicitly converted into void const*, which prints address of string literal.

Once the temporary invokes the member function, the remaining chained << may invoke non-member overloads, because the member function return std::ostream& which now can bind to the first argument to the non-member overloads of operator<<. So this following code would print an address (instead of "XYZ") followed by string "ABC":

static_cast<std::ostringstream&>(std::ostringstream() << "XYZ" << "ABC").str()

Online demo:

C++11

In C++11, this issue has been fixed by adding a non-member function (27.7.3.9) which takes the first argument as rvalue reference which then forwards the call to the appropriate function, either member or non-member. So it prints XYZ followed by ABC:

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 1
    The non-member overloads cannot be used for the first `<<`. After that, you have the `ostream&` (non-const) which was returned by the first `<<`. The usual solution is to start with `std::ostringstream().flush() << ...`. `ostream::flush` is a member function which returns a `ostream&`, and of course, it won't actually do anything on a stream which you haven't yet written to. – James Kanze Oct 29 '13 at 18:10
  • @JamesKanze: I just came to know that there C++11 behaves differently in this case. So what I described before is true for C++03 only. With C++11, we don't need `flush()` trick anymore! – Nawaz Oct 30 '13 at 14:07
  • I seem to remember the same thing, but I can't find what has changed in the standard. (I think it is somehow related to rvalue-references; I don't know why.) – James Kanze Oct 31 '13 at 09:18
  • @JamesKanze: Yes, 27.7.3.9 has introduced rvalue-references for output streams (and there is rvalue-references for input streams also). – Nawaz Oct 31 '13 at 09:30
  • @Nawaz I know that to Microsoft the standard is kind of a suggestion, but in Visual Studio 2010 I see your C++11 code work. I thought that Visual Studio 2010 was C++03? – Jonathan Mee Dec 23 '14 at 15:31
  • Notice: latest clang++ compiler (9.0.0) complains about non-const lvalue reference so to cast we need `static_cast(std::ostringstream() << etc).str()` – Krzysztof Dec 13 '19 at 10:40
  • On MSVC 2019 it gives `error C2440: 'static_cast': cannot convert from '_Ostr' to 'std::ostringstream &' with [ _Ostr=std::ostringstream ]`, note: static_cast and safe_cast to reference can only be used for valid initializations or for lvalue casts between related classes. Note that @Krzysztof's solution above works. – A T Apr 23 '21 at 01:55
8

basic_osstream::operator<< returns a basic_ostream, not a ostringstream. The compiler is not lying to you.

I would rather create a StringBuilder type device.

class StringBuilder
{
public:
    template <typename T> inline StringBuilder& operator<<(const T& t)
    {
        mStream << t;
        return * this;
    }
    inline std::string get() const
    {
        return mStream.str();
    }
    inline operator std::string () const
    {
        return get();
    }
private:
    std::stringstream mStream;
};

Now you can:

std::string myString (StringBuilder() << myInt << " " << myFloat /*...*/);

There are also similar devices in Boost -- you would do well to use those instead if it is available to you.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
0

I had this problem too, and John answer seems fine, thanks a lot! - I tried to build a string by using the operator '<<' onto a 'stringstream', and it did compiled & worked fine with 'clang' on macOS but not with 'gcc' on ubuntu, (compiling with -std=c++11 on modern updated compilers), it looks like in the Mac machine the operator is returning a stringstream instead of a basic_ostream......

Here is a minimal program which does not compiles with g++ but compiles with clang (experimental concepts), as tested in the Compiler Explorer (godbolt.org):

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

int main()
{
   using namespace std;
   cout << ((stringstream() << "hola").str()) << endl ;
}
user5122888
  • 309
  • 3
  • 5