В действительности хотелось бы реализовать advance как-то так:
template
void advance(IterT& iter, DistT d)
{
if ( iter является итератором с произвольным доступом ) {
iter += d; // использовать итераторную арифметику
} // для итераторов с произвольным доступом
else {
if(d>=0) {while (d–) ++iter;} // вызывать ++ или – в цикле
else {while(d++) –iter;} // для итераторов других категорий
}
}
Но для этого нужно уметь определять, является ли iter итератором с произвольным доступом, что, в свою очередь, требует знания о том, что его тип – IterT – относится к категории итераторов с произвольным доступом. Другими словами, нам нужно получить некоторую информацию о типе. Именно для этого и служат характеристики (traits): получить информацию о типе во время компиляции.
Traits – это не ключевое слово и не предопределенная конструкция в C++; это техника и соглашение, которому следуют программисты. Одним из требований, предъявляемых к ней, является то, что она должна одинаково хорошо работать и для встроенных типов, и для типов, определяемых пользователем. Например, при вызове для обычного указателя (типа const char*) или значения типа int операция advance должна работать, а это значит, что техника характеристик должна быть применима и к встроенным типам.
Тот факт, что характеристики должны работать со встроенными типами, означает, что нельзя рассчитывать на размещение специальной информации внутри типа, потому что в указателях никакую информацию не разместишь. Поэтому характеристическая информация о типе должна быть внешней по отношению к типу. Стандартная техника заключается в помещении ее в шаблон, для которого существует одна или несколько специализаций. Для итераторов в стандартной библиотеке существует шаблон iterator_traits:
template // шаблон для информации
struct iterator_traits; // о типах итераторов
Как видите, iterator_traits – это структура. По соглашению характеристики всегда реализуются в виде структур. Другое соглашение заключается в том, что структуры, используемые для их реализации, почему-то называются классами- характеристиками.
Смысл iterator_traits состоит в том, что для каждого типа IterT определяется псевдоним typedef iterator_category для структуры iterator_traits. Этот typedef идентифицирует категорию, к которой относится итератор IterT.
Реализация этой идеи в iterator_traits состоит из двух частей. Первая – вводится требование, чтобы все определяемые пользователем типы итераторов имели внутри себя вложенный typedef с именем iterator_category, который задает соответствующую структуру-тэг. Например, итераторы deque являются итераторами с произвольным доступом, поэтому класс итераторов deque должен выглядеть примерно так:
template <���…>
class deque {
public:
class iterator {
public:
typedef random_access_iterator_tag iterator_category;
...
};
...
};
Итераторы для контейнеров list являются двунаправленными, поэтому для них объявление выглядит так:
template <���…>
class list {
public:
class iterator {
public:
typedef bidirectional_iterator_tag iterator_category;
};
...
};
В шаблоне iterator_traits просто повторен находящийся внутри класса итератора typedef:
// iterator_category для типа IterT – это то, что сообщает о нем сам IterT
// см. в правиле 42 информацию об использовании “typedef typename”
template
struct iterator_traits {
typedef typename IterT::iterator_category iterator_category;
...
};
Это работает с пользовательскими типами, но не подходит для итераторов, которые являются указателями, потому что не существует указателей с вложенными typedef. Поэтому во второй части шаблона iterator_traits реализована поддержка итераторов, являющихся указателями.
С этой целью iterator_traits представляет частичную специализацию шаблонов для типов указателей. Указатели ведут себя как итераторы с произвольным доступом, поэтому в iterator_traits для них указана именно эта категория:
template // частичная специализация шаблона
struct iterator_traits // для встроенных типов указателей
Читать дальше
Конец ознакомительного отрывка
Купить книгу