Функция, которая принимает итератор STL через ЛЮБОЙ контейнер элементов определенного типа

Как бы я определил функцию, которая принимает в качестве ввода итератор по любому типу контейнера STL, но только для тех, которые относятся к конкретному шаблонизированному типу. Например:

Любой итератор формы std::list<Unit*>::iterator или std::vector<Unit*>::iterator

Я бы просто определил функцию для принятия std::list<Unit*>::iterator, но если мы переключимся на другой контейнер STL, я не хочу менять свой код.

Есть ли способ сделать это с помощью шаблонов или иначе?

Ответы

Ответ 1

Вы можете использовать конструкцию SFINAE, такую ​​как boost:: enable_if, который проверяет, действительно ли вложенный typedef iterator::value_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 */
}

Это то, что я понимаю из "функции, которая принимает в качестве входных данных итератор над любым типом контейнера STL, но только для тех, которые имеют определенный шаблонный тип", но моя интерпретация может быть неправильной.

Ответ 2

В дополнение к существующим ответам, основанным на SFINAE, более простым приближением было бы просто определить функцию, чтобы взять произвольный тип шаблона в качестве итератора:

template <typename Iter>
void function(Iter first, Iter last){
  Unit* val = *first;
}

У этого есть несколько недостатков. В отличие от решения SFINAE (например, boost::enable_if), это не дает вам именно то, что вы просили. Это компилируется до тех пор, пока объект типа Iter может быть разыменован, получив значение, конвертируемое в Unit*, что не совсем то же самое. У вас нет гарантии, что Iter является полностью STL-совместимым итератором (это может быть просто другой тип, который определяет operator*), или что его тип значения Unit* точно.

С другой стороны, это намного проще.

Ответ 3

Вы хотите только перебирать контейнеры my_special_type? В этом случае:

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)
{
    // ...
}

Ответ 4

Другой способ с шаблонами - вызвать статическое утверждение, если условие не выполнено.

#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
}

Если вы хотите покончить с шаблонами, тогда также можно написать "any_iterator", используя стирание типа. Например, например: http://stlab.adobe.com/classadobe_1_1any__iterator.html

Ответ 5

Ну, я бы использовал простой typedef в том случае, когда вам нужен только один тип контейнера в тот момент (если я правильно понял вашу учетную запись, это так)

template <class T>
class ContainerIterator
{
  Container();
  public:
    typedef std::list<T>::iterator type;
}

//usage:
void function(ContainerIterator<YourType>::type begin, ContainerIterator<YourType>::type end)
{
  //...
}

чтобы позже переключить контейнер, просто измените тип контейнера в typedef.