На этом шаге мы рассмотрим внутренее придставление указателей.
Итераторы делятся на категории в соответствии со своими специфическими возможностями. Иногда бывает удобно и даже необходимо переопределить поведение итератора для других категорий. Такое переопределение выполняется при помощи механизма тегов и трактовок итераторов.
Для каждой категории итераторов в стандартной библиотеке C++ определяется тег, предназначенный для ссылок на данную категорию:
namespace std { struct output_iterator_tag { }; struct input_iterator_tag { }; struct forward_iterator_tag : public input_iterator_tag { }; struct bidirectional_iterator_tag : public forward_iterator_tag { }; struct random_access_iterator_tag : public bidirectional_iterator_tag { }; }
Обратите внимание на наследование. Так, из представленной иерархии следует вывод, что любой прямой итератор forward_iterator_tag является разновидностью итератора ввода input_iterator_tag. Но заметьте, что тег прямых итераторов объявлен производным только от тега итераторов ввода, а не от тега итераторов вывода. Соответственно произвольный прямой итератор не является разновидностью итератора вывода. Для прямых итераторов определены особые требования, которые отличают их от итераторов вывода.
При написании универсального кода важна не только категория итераторов. Например, вам может потребоваться тип элементов, на которые ссылается итератор. По этой причине в стандартной библиотеке C++ имеется специальный шаблон структуры, позволяющий определить варианты трактовки итераторов (iterator traits). Эта структура содержит наиболее существенную информацию об итераторе и предоставляет общий интерфейс ко всем определениям типов, ассоциированным с итератором (категория, тип элементов и т. д.):
namespace std { template <class T> struct iterator_traits { typedef typename T::value_type va1ue_type; typedef typename T::difference_type difference_type: typedef typename T::iterator_category iterator_category; typedef typename T::pointer pointer; typedef typename T::reference reference; }; }
В этом шаблоне Т представляет тип итератора. Это означает, что в программе для произвольного итератора можно получить доступ к его категории, типу элементов и т. д. Например, следующее выражение определяет тип значений, перебираемых итератором типа Т:
typename std::iterator_traits<T>::va1ue_type
У такой структуры есть два важных достоинства:
Например, специализация применяется для обычных указателей, которые тоже могут использоваться в качестве итераторов:
namespace std { template <class T> struct iterator_traits<T*> { typedef T value_type; typedef ptrdiff_t difference_type; typedef random_access_iterator_tag iterator_category; typedef T* pointer; typedef T& reference; }; }
Любой тип "указателя на Т" определяется как относящийся к категории итераторов произвольного доступа. Соответствующая частичная специализация существует и для константных указателей (const T*).
Со следующего шага мы начнем знакомиться с написанием унифицированных функций для итераторов.