Skip to main content
4 of 6
added 13 characters in body
Snowhawk
  • 6.3k
  • 1
  • 18
  • 33

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.

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 Seperator& seperator) {
  using ResultType = std::iterator_traits<InputIterator>::value_type;
  ResultType result;

  if (first != last) {
    result += *first;
    ++first;
  }

  for (; first != last; ++first) {
    result += seperator;
    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';
}
Snowhawk
  • 6.3k
  • 1
  • 18
  • 33