I assume that you probably want to users to be able to pass ForwardIterator
InputIterator
. I will explain why I made the assumption.
According to assumption, you can rename the T
into InputIt
. Iterators denoting range are usually called first
and last
, even though last is not actually last.
template<typename InputIt>
std::string join(InputIt first,
InputIt last,
std::string separator,
std::string concluder)
I've never seen iterators being passed as const. Probably you wanted iterators referring to const objects? It is common to say const iterator, but it doesn't mean that iterators are const.
count
and length
variables are somewhat odd. The pair of iterators should denote the range needed, and it is work of the operator!=()
to check if the end is hit.
for (T iter = begin; iter != end; ++iter, ++count)
Never seen traversing the range like that. People usually use this:
while (first != last)
{
ss << *iter++;
ss << separator;
}
It will need to be tweaked a bit to not output the separator at the end. I'll use what @Zeta suggested (awesome idea):
We'll check if the range is empty, then print the first element:
if (first == last)
{
return concluder;
}
std::stringstream ss;
ss << *first;
++first;
And then we swap sequence of output in the loop, so we output separator first, then the element. This way, there won't be separator at the end:
while (first != last)
{
ss << separator;
ss << *first;
++first;
}
Even though preincrement is slightly faster than postincrement, my benchmarking program didn't show significant difference (it is less than 1%). Nevertheless, I've used preincrement to not make life harder for custom iterators.
Then we simply output concluder:
ss << concluder;
Rather than creating multiple overloads use default arguments:
template <typename InputIt>
std::string join(InputIt first, InputIt last, const std::string& separator = ", ", const std::string& concluder = "")
And const correctness, of course.
Full code:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::for_each;
using std::vector;
template<typename InputIt>
std::string join(InputIt first,
InputIt last,
const std::string& separator = ", ",
const std::string& concluder = "")
{
if (first == last)
{
return concluder;
}
std::stringstream ss;
ss << *first;
++first;
while (first != last)
{
ss << separator;
ss << *first;
++first;
}
ss << concluder;
return ss.str();
}
int main() {
vector<vector<int>> matrix = {
{ 1, 2, 3 },
{ 4, 5 },
{},
{ 10, 26, 29 }
};
for_each(matrix.cbegin(),
matrix.cend(),
[](std::vector<int> a) {
cout << join(a.cbegin(), a.cend()) << endl;
});
}
I don't really like using
, but I don't think it is of any importance here.
Using input itertors will yield basic exception safety, since the range is invalid after it being passed once. For other supported iterator types the function has strong exception safety guarantee.