How to make function argument container independent
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 thisstd::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 tostd::vector<std::string>::const_iterator it = vec.begin()
the erro开发者_开发技巧r 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.
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.
Following STL practice, I would recommend using two iterators for input parameters, instead of a container (for obvious reason of being able to work with only a part of a container, and generally with any sequence defined by iterators):
template<typename InputIterator>
std::string convert2Str(InputIterator first, InputIterator last)
{
std::ostringstream sStream;
for (InputIterator it = first; it != last; ++it) {
sStream << *it << " ";
}
return sStream.str();
}
In case you need the type of contained objects, use
typedef typename std::iterator_traits<InputIterator>::value_type T;
ADDED: You then can use the function as follows:
std::vector<int> int_vec;
std::list<float> f_list;
std::deque<std::string> str_deq;
// put something into the containers here
std::cout<< convert2Str(int_vec.begin(), int_vec.end()) <<std::endl;
std::cout<< convert2Str(f_list.begin(), f_list.end()) <<std::endl;
std::cout<< convert2Str(str_deq.begin(), str_deq.end()) <<std::endl;
Note that you cannot iterate over std::queue; but if you really need it, the standard guarantees enough support for a do-it-yourself solution. See more info here: std::queue iteration.
It's easiest if you templatize on the container type only; the value type is stored in all standard, Boost and Qt containers as the typedef
member value_type
. std::copy
and ostream_iterator
allow you to skip the lengthy iterator declarations.
template <typename Container>
std::string convert2Str(Container const &cont)
{
std::ostringstream s;
std::copy(cont.begin(), cont.end(),
std::ostream_iterator<typename Container::value_type>(s, " "));
return s.str();
}
The typename
is necessary to avoid ambiguity. Recent versions of GCC will warn you when you omit this keyword.
Use this. You need the typename
part in order to tell the compiler that it should consider T::const_iterator
a type while parsing, it can't really know that this is true until you actually call the function passing some T
that has a const_iterator
member type.
template<typename T>
std::string convert2Str(T const& cont)
{
std::ostringstream sStream;
for (typename T::const_iterator it = cont.begin(); it != cont.end(); ++it) {
sStream << *it << " ";
}
return sStream.str();
}
I think this should work:
template<typename T>
std::string convert2Str(T const& container)
{
std::ostringstream sStream;
for (typename T::const_iterator i= container.begin(); i != container.end(); ++i) {
sStream << *i << " ";
}
return sStream.str();
}
Demo : http://ideone.com/9pUVV
精彩评论