C++ – How to make function argument container independent

c++iteratortemplates

I'm writing a utility function which will take a vector of elements (could be string, int, double, char) and concatenate into a single string and return it. It looks like this:

template<typename T>
std::string convert2Str(std::vector<T> const& vec) 
{
   std::ostringstream sStream; 
   for (size_t k=0; k<vec.size(); ++k) {
      sStream << vec[k] << " "; 
   }
   return sStream.str(); 
}

I would like to make this function more generic:

  • First use iterators instead of using indices for the vector<T>. I tried this
    std::vector<T>::const_iterator it = vec.begin() before the loop and the compiler gave me an error:
    : error: expected ; before it
    When I change the above defintions to std::vector<std::string>::const_iterator it = vec.begin() the error goes away. So, it looks like I'm not following correct syntax, please let me know what it is
  • Second is to make the function more generic by making the first argument container independent. Given any container (vector, list, queue, deque, etc.) I want to do the same thing as above. I tried searching for this in stackoverflow and did not find satisfactory answer.

Best Solution

Step 1, as you said, use iterators:

template<typename T>
std::string convert2Str(std::vector<T> const& vec) 
{
   typedef std::vector<T> container;
   std::ostringstream sStream; 
   for (typename container::const_iterator it = vec.begin(); it != vec.end(); ++it) {
      sStream << *it << " "; 
   }
   return sStream.str(); 
}

Step 2, make the template argument the container type instead of the element type (you can get the element type back with value_type:

template<typename container>
std::string convert2Str(container const& vec)
{
   typedef container::value_type T; // if needed
   std::ostringstream sStream; 
   for (typename container::const_iterator it = vec.begin(); it != vec.end(); ++it) {
      sStream << *it << " "; 
   }
   return sStream.str(); 
}

In C++0x, this gets even simpler (and typename is not needed):

template<typename container>
std::string convert2Str(container const& vec)
{
   using std::begin;
   using std::end;
   std::ostringstream sStream;
   for (auto it = begin(vec); it != end(vec); ++it) {
      typedef decltype(*it) T; // if needed
      sStream << *it << " "; 
   }
   return sStream.str(); 
}

Among other advantages, std::begin and std::end work for raw arrays.

Related Question