Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is std::advance implemented to change behavior on iterator type?

What we know about std::advance is the following:

template <class InputIterator, class Distance>
void advance (InputIterator& i, Distance n);

Purpose

Advances the iterator i by n elements.

If i is a Random Access Iterator, the function uses once operator+ or operator-, otherwise, the function uses repeatedly the increase or decrease operator (operator++ or operator--) until n elements have been advanced.


My question is the following: How is std::advance implemented such that it recognizes if it is a Random Access Iterator or not? How does it know it can use operator+ instead of operator++?

like image 346
Narek Avatar asked Mar 12 '13 18:03

Narek


People also ask

What is STD advance?

std::advance in C++ std::advance advances the iterator 'it' by n element positions. Syntax : template void advance (InputIterator& it, Distance n); it : Iterator to be advanced n : Number of element positions to advance. This shall only be negative for random-access and bidirectional iterators.

What type does std :: distance return?

std::distance Returns the number of elements between first and last . The behavior is undefined if last is not reachable from first by (possibly repeatedly) incrementing first .


2 Answers

Through iterator_traits and tag dispatch:

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());
}

Note that iterator_category is a type (one of std::input_iterator_tag etc.), so iterator_category() is not a function call; it's an expression that constructs a temporary prvalue of that type. The appropriate overload of advance_impl is then selected by normal overload resolution. This is called tag dispatch. Equivalently one could write:

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);
}

The overloads of advance_impl are receiving as their third argument an unnamed argument that is an instance of their chosen tag type.

like image 113
ecatmur Avatar answered Oct 06 '22 08:10

ecatmur


I would imagine it may use std::iterator_traits::iterator_category to figure out what the type of the iterator is.

Based on that, it can decide how to advance things.

like image 27
Tony The Lion Avatar answered Oct 06 '22 06:10

Tony The Lion