std::[o]stringstream
vs std::string::append()
/std::string::operator+=()
With std::string::append()
, you'll usually pay the initial allocation, and with each append you'll pay for a data copy and possibly an allocation. Constructing std::ostringstream
is more complex compared to string. You have to initialize the stream, the states, and the locale. Every std::ostringstream::operator<<()
call accrues a possible allocation, a data copy, and state checks. Once you've completed that, you have to invoke std::stringstream::str()
to use the string, which involves a copy (depending on implementation).
With C++11 introducing non-member std::to_string()
and std::stoXX()
conversion functions and all the various format libraries out there, I haven't really found a use for std::ostringstream
recently.
Explicit Typing
std::stringstream
inherits from both std::istringstream
and std::ostringstream
. You'll find that rarely will you have to both read and write from a stream, so be explicit in your intent and use the std::[i|o]stringstream
type.
Interface
vectorSubstr()
could be confusing for some people. Does it return a std::vector<std::string>
of substrings? Consider other names like to_string()
, join()
, concatenate()
.
Prefer writing range-based algorithms using the Iterator pattern that is heavily used by the STL.
If you do not plan on changing the contents of a parameter, const
-qualify it.
Your parameter for the container is taken By-Value. Copies on containers (and non-primitives) can be expensive, so pass those arguments By-Reference (&
).
std::string join(const std::vector<std::string> &input, const char separator)
Algorithm
You can reduce the amount of branching you have to do by handling the first element case, then your delimiter becomes naturally infixed.
Putting It Together
template <typename InputIterator, typename Separator>
inline typename std::iterator_traits<InputIterator>::value_type
join(InputIterator first, InputIterator last, const Separator& separator) {
using ResultType = std::iterator_traits<InputIterator>::value_type;
ResultType result;
if (first != last) {
result += *first;
++first;
}
for (; first != last; ++first) {
result += separator;
result += *first;
}
return result;
}
int main() {
const std::vector<std::string> inputs = {"a", "bcd", "efghijklm", "nopqrstuvwxyz"};
const auto first = std::next(std::begin(inputs), 2);
const auto last = std::end(inputs);
std::cout << join(first, last, ", ") << '\n';
}