Как проверить, что прошедший Iterator является итератором с произвольным доступом?
У меня есть следующий код, который выполняет некоторую арифметику итератора:
template<class Iterator>
void Foo(Iterator first, Iterator last) {
typedef typename Iterator::value_type Value;
std::vector<Value> vec;
vec.resize(last - first);
// ...
}
Выражение (last - first)
работает (AFAIK) только для итераторов произвольного доступа (например, из vector
и deque
). Как я могу проверить код, который переданный итератор отвечает этому требованию?
Ответы
Ответ 1
Если Iterator
- итератор с произвольным доступом, то
std::iterator_traits<Iterator>::iterator_category
будет std::random_access_iterator_tag
. Самый простой способ реализовать это, вероятно, создать второй шаблон функции и вызвать Foo
:
template <typename Iterator>
void FooImpl(Iterator first, Iterator last, std::random_access_iterator_tag) {
// ...
}
template <typename Iterator>
void Foo(Iterator first, Iterator last) {
typedef typename std::iterator_traits<Iterator>::iterator_category category;
return FooImpl(first, last, category());
}
Это имеет то преимущество, что вы можете перегрузить FooImpl
для разных категорий итераторов, если хотите.
Скотт Мейерс обсуждает эту технику в одной из эффективных книг на C++ (я не помню, какой из них).
Ответ 2
В дополнение к отправке тегов вы можете напрямую сравнивать категорию с std::random_access_iterator_tag
с помощью std::is_same_v
:
using category = typename std::iterator_traits<Iterator>::iterator_category;
if constexpr (std::is_same_v<category, std::random_access_iterator_tag>) {
vec.resize(last - first);
}
Иногда это может привести к более четкому и сжатому коду, особенно если только небольшая часть вашей реализации (например, сохранение размера вектора) зависит от категории итератора.