Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use an Anonymous Stringstream to Construct a String

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?

like image 730
Jonathan Mee Avatar asked Oct 29 '13 17:10

Jonathan Mee


2 Answers

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:

  • GCC output without -std=c++11
  • Clang output without-std=c++11

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:

  • GCC output with-std=c++11
  • Clang output with -std=c++11
like image 158
Nawaz Avatar answered Oct 24 '22 01:10

Nawaz


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.

like image 31
John Dibling Avatar answered Oct 24 '22 01:10

John Dibling