开发者

Function that takes an STL iterator over ANY container of a elements of a specific type

开发者 https://www.devze.com 2023-01-29 01:27 出处:网络
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:

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号