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?
stringstream ss << "Number of people is " << numPeople;
Why can't I assign the
stringstream
value at the same time I declare it?
This is similar to hoping this would work:
int x + 3 + 9;
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 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 independently first to give a sane value to store in x
. The compiler's behaviour for operator +
on int
s does what we want: it produces the int
result we then want 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
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 the stringstream
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 type ostream&
. 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.
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 stringstream
s 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.
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());
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.
C++11 introduced to_string()
overloads which are convenient if you have an integral value or two to concatentate with or into a string
:
std::string s = "Number of people is " + std::to_string(numPeople);
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 independent std::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 temporary std::string
s will take time to deallocate during destruction.
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 the std::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.