Что мы знаем о std::advance
является следующим:
template <class InputIterator, class Distance>
void advance (InputIterator& i, Distance n);
Цель
Продвигает итератор i
от n
элементы.
Если i
является итератором произвольного доступа, функция использует один раз operator+
или же operator-
в противном случае функция многократно использует оператор увеличения или уменьшения (operator++
или же operator--
) до тех пор n
элементы были продвинуты.
Мой вопрос заключается в следующем:
Как std::advance
реализован так, что он распознает, если it
такое итератор произвольного доступа или нет? Как он знает, что может использовать operator+
вместо operator++
?
Через iterator_traits
а также отправка тегов:
template<class InputIterator, class Distance>
void advance_impl(InputIterator& i, Distance n, std::random_access_iterator_tag) {
i += n;
}
template<class InputIterator, class Distance>
void advance_impl(InputIterator& i, Distance n, std::bidirectional_iterator_tag) {
if (n < 0) {
while (n++) --i;
} else {
while (n--) ++i;
}
}
template<class InputIterator, class Distance>
void advance_impl(InputIterator& i, Distance n, std::input_iterator_tag) {
assert(n >= 0);
while (n--) ++i;
}
template<class InputIterator, class Distance>
void advance (InputIterator& i, Distance n) {
advance_impl(i, n,
typename std::iterator_traits<InputIterator>::iterator_category());
}
Обратите внимание, что iterator_category
это тип (один из std::input_iterator_tag
и т. д.) iterator_category()
не является вызовом функции; это выражение, которое создает временное значение этого типа. Соответствующая перегрузка advance_impl
затем выбирается нормальным разрешением перегрузки. Это называется отправка тегов. Эквивалентно можно написать:
template<class InputIterator, class Distance>
void advance (InputIterator& i, Distance n) {
typename std::iterator_traits<InputIterator>::iterator_category the_tag;
advance_impl(i, n, the_tag);
}
Перегрузки advance_impl
получают в качестве третьего аргумента безымянный аргумент, который является экземпляром выбранного типа тега.
Я полагаю, это может использовать std::iterator_traits::iterator_category
выяснить, какой тип итератора.
Исходя из этого, он может решить, как продвигать вещи.