How would I define a function that takes as input an iterator over any type of STL container, but only to those of a specific templated type. For example:
Any iterator of the form std::list<Unit*>::iterator
or std::vector<Unit*>::iterator
I would just define the function to take std::list<Unit*>::iterator
, but if we switch to a different STL container, I don't want to 开发者_运维技巧have to change my code.
Is there a way to do this with templates or otherwise?
You could use a SFINAE construct such as boost::enable_if which verifies if the nested typedef iterator::value_type
is indeed of the appropriate type.
template<class T, class Iterator>
typename boost::enable_if<boost::is_same<typename Iterator::value_type, T> >::type
f(Iterator i)
{
/* ... */
}
int main()
{
std::list<int> l;
std::vector<int> v;
f<int>(l.begin()); // OK
f<int>(v.begin()); // OK
std::vector<float> v2;
f<int>(v2.begin()); /* Illegal */
}
This is what I understand from "a function that takes as input an iterator over any type of STL container, but only to those of a specific templated type", but my interpretation might be wrong.
In addition to the existing answers relying on SFINAE, a simpler approximation would be to simply define the function to take an arbitrary template type as the iterator:
template <typename Iter>
void function(Iter first, Iter last){
Unit* val = *first;
}
This has a few downsides. Unlike the SFINAE solution (such as boost::enable_if
), this doesn't give you exactly what you asked for. This compiles as long as an object of type Iter
can be dereferenced yielding a value convertible to Unit*
, which isn't quite the same thing. You have no guarantee that Iter
is a completely STL-compliant iterator (it may just be another type which defines operator*
), or that its value type is Unit*
precisely.
On the other hand, it's much simpler.
You only want to iterate over containers of my_special_type
? In that case:
template <bool, typename T>
struct enable_if;
template <typename T>
struct enable_if<true, T>
{
typedef T type;
};
template <typename T, typename U>
struct is_same
{
enum {value = false};
};
template <typename T>
struct is_same<T, T>
{
enum {value = true};
};
template <typename Iter>
typename enable_if<is_same<typename Iter::value_type, your_special_type>::value,
void>::type
function(Iter begin, Iter end)
{
// ...
}
Another way with templates is to trigger a static assertion if the condition is not met.
#include <iterator>
#include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>
template <class Type, class Iter>
void foo(Iter from, Iter to)
{
BOOST_STATIC_ASSERT((boost::is_same<typename std::iterator_traits<Iter>::value_type, Type>::value));
//...
}
int main()
{
int arr[10];
foo<int>(arr, arr + 10); //OK
foo<double>(arr, arr + 10); //triggers static assertion
}
If you want to do away with templates, then it is also possible to write an "any_iterator" using type erasure. For example, like this on: http://stlab.adobe.com/classadobe_1_1any__iterator.html
Well i would use a simple typedef in the case that you only need one container-type at the time (if i understood your usecase correctly this is the case)
template <class T>
class ContainerIterator
{
Container();
public:
typedef std::list<T>::iterator type;
}
//usage:
void function(ContainerIterator<YourType>::type begin, ContainerIterator<YourType>::type end)
{
//...
}
to switch the container later on, simply change the container type in the typedef.
精彩评论