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?
The stringbuf has to be initialized before values can be added to it. stringstream is an object.
By doing:
You are essentially allowing the application to allocate space to handle the to-be added values.
This is similar to hoping this would work:
The problem
When you're both defining and giving a value to an object, C++ lets you call the constructor, which requires a comma-separated list of expressions. For convenience the
Type variable = value;
notation is elided to callType variable(value);
, but only works for a single value.For
int
, you can easily correct the code:...and it works because "3 + 9" can be evaluated independently first to give a sane value to store in
x
. The compiler's behaviour for operator+
onint
s does what we want: it produces theint
result we then want to store inx
. But if you try that forstringstream
......it won't work, because
"Number of people is " << numPeople
needs to be evaluated first but 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 thestringstream
constructor. The problem is that the compiler's still trying to apply the bitwise shift operator, which only makes sense for numbers, as the overloads for<<
that we'd want to apply require a left-hand argument of typeostream&
. C++ requires the value being evaluated on the right of=
to be evaluated independently of the assignment that's eventually done with the resultant value, and at that point the type of the variable being constructed is not relevant to the way evaluation is attempted on the expression being assigned.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 thestringstream
's constructor, but for that you need... astringstream
. You can actually pull that off with a temporarystringstream
:The cast is unfortunately needed because the
operator<<
overloads handlestringstream
s via references to theirostream
base class, returning anostream&
, so you need to cast back to thestringstream
type manually, so you can then invoke thestd::stringstream
move constructor...The complete one-liner construction is then...
...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...
FWIW, you could also use the macro to create strings...
An (arguably) better practice
Just create the
stringstream
- optionally providing a singlestring
to the constructor - then useoperator<<
in a second statement:This is much easier to read. With move construction, after optimisation there's likely no performance reasons for preferring two statements.
An alternative
C++11 introduced
to_string()
overloads which are convenient if you have an integral value or two to concatentate with or into astring
:This may be inefficient though (check your compiler(s) optimisation abilities if you care): each
std::to_string()
is likely to dynamically allocate a buffer for an independentstd::string
instance, then the individual concatenations may involve extra copying of text, and the original dynamically-allocated buffers may need to be enlarged, then most of those temporarystd::string
s will take time to deallocate during destruction.It was worse in C++03
C++03 lacked move constructors, so it was necessary to use the
std::ostringstream::str()
member function on the temporary to get an extra deep-copy of thestd::string
with which to construct the namedstringsteam
...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.