Функция, которая принимает итератор 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.