Dragons_Lair5 Dragons_Lair5 - 1 month ago 10
C++ Question

How to initialize a std::stringstream?

I need to concatenate a string with integers. To do that I'm using

stringstream
in the following way:

int numPeople = 10;
stringstream ss;
ss << "Number of people is " << numPeople;


And that worked. But I was trying to do it in the way below:

int numPeople = 10;
stringstream ss << "Number of people is " << numPeople;


And I was getting the following error: "expected initializer before '<<' token"

Why was I getting this error? Why can't I assign the
stringstream
value at the same time I declare it?

Answer
stringstream ss << "Number of people is " << numPeople;

Why cant i assign stringstream value in the same time i declare it?

This is similar to hoping this would work:

int x + 3 + 9;

The problem

C++ only lets you provide a value to the constructor when creating an object, and the constructor accepts a comma-separated list of expressions. For convenience the Type variable = value; notation is elided to call Type variable(value);, but only works for a single value.

For int, you can easily correct the code:

int x = 3 + 9;

...and it works because "3 + 9" can be evaluated first to give a sane value to store in x. But if you try that for stringstream...

stringstream ss = "Number of people is " << numPeople;  // BROKEN

...it won't work, because "Number of people is " << numPeople is illegal - you'll get an error like "error C2296: '<<' : illegal, left operand has type 'const char [20]'" - it won't give a useful value for the stringstream constructor.

A solution

It's a bit of a chicken-and-egg problem here, because you kind of need to combine the right-hand values you want in the stringstream to call the stringstream's constructor, but for that you need... a stringstream. You can actually pull that off with a temporary stringstream:

static_cast<std::ostringstream&&>(std::ostringstream() << "Number of people is " << numPeople)

The cast is unfortunately needed because the operator<< overloads handle stringstreams via references to their ostream base class, returning an ostream&, so you need to cast back to the stringstream type manually, so you can then invoke the std::stringstream move constructor...

The complete one-liner construction is then...

std::stringstream ss(static_cast<std::ostringstream&&>(std::ostringstream() << "Number of people is " << numPeople));

...but that's too hideous to contemplate.

Making the solution (arguably) less hideous

Depending on your sensibilities, you may feel a macro helps or is worse...

#define OSS(VALUES) \
    static_cast<std::ostringstream&&>(std::ostringstream() << VALUES)

std::stringstream ss(OSS("Number of people is " << numPeople));

FWIW, you could also use the macro to create strings...

std::string s(OSS("Number of people is " << numPeople).str()); 

An (arguably) better practice

Just create the stringstream - optionally providing a single string to the constructor - then use operator<< in a second statement:

std::stringstream ss;
ss << "Number of people is " << numPeople;

This is much easier to read. With move construction, after optimisation there's likely no performance reasons for preferring two statements.

It was worse in C++03

C++03 lacked move constructors, so it was necessary to use the .str() member on the temporary to get a string with which to construct the named stringsteam...

stringstream ss(static_cast<std::ostringstream&>(std::ostringstream() << "Number of people is " << numPeople).str());

With this C++03 code, there's a likelihood of duplicate dynamic memory allocations and a copy of content, so construction-followed-by-streaming was a better bet.

Comments