Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
STL5 / lab4-iterators / lab4-iterators.doc
Скачиваний:
13
Добавлен:
10.04.2015
Размер:
299.01 Кб
Скачать

Функции advance и distance

STL обеспечивает две шаблонных функции advance и distance:

template <class InputIterator, class Distance>

inline void advance(InputIterator& i, Distance n);

template <class InputIterator, class Distance>

inline void distance(InputIterator first, InputIterator last, Distance& n);

Эти функции позволяют продвинуть итератор любого типа на произвольное количество позиций вперед (advance), а также определить расстояние между двумя итераторами (distance). Однако они реализованы по-разному для итераторов произвольного доступа и для остальных типов, в результате чего для итераторов произвольного доступа они выполняются за постоянное время (такие итераторы сами предоставляют подобные функции), а для других типов – за линейное (например,advance() наnпозиций – этоnопераций ++)

Использование:

vector<int> v;

list<int> l;

// ... Заполнить коллекции

vector<int>::iterator vi=v.begin(), vi2=vi;

list<int>::iterator li=l.begin(), li2=li;

vector<int>::difference_type vdist; // См. Главу 7

list<int>::difference_type ldist;

advance(vi,5);// Выполнится за постоянное время (1 операция +)

vdist=distance(vi,vi2);

advance(li,5);// Выполнится за 5 операций ++

ldist=distance(li,li2);

Теги и свойства итераторов5

Часто бывает нужно для повышения универсальности и независимости алгоритма получить непосредственно из итератора такие его свойства, как: тип, тип итерируемого контейнера, тип разности итераторов и т.д. Для этого предназначены теги (iteratortags) и свойства (traits) итераторов

Рассмотрим определения из файла <xutility>:

// ITERATOR TAGS

structinput_iterator_tag

{ // identifying tag for input iterators

};

structoutput_iterator_tag

{ // identifying tag for output iterators

};

structforward_iterator_tag

: public input_iterator_tag

{ // identifying tag for forward iterators

};

structbidirectional_iterator_tag

: public forward_iterator_tag

{ // identifying tag for bidirectional iterators

};

structrandom_access_iterator_tag

: public bidirectional_iterator_tag

{ // identifying tag for random-access iterators

};

// Часть кода опущена

// TEMPLATE CLASS iterator

template<class _Category, // Сюда подставляется один из ..._iterator_tag

class _Ty,

class _Diff = ptrdiff_t,

class _Pointer = _Ty *,

class _Reference = _Ty&>

structiterator

{ // base type for all iterator classes

typedef _Category iterator_category;

typedef _Ty value_type;

typedef _Diff difference_type;

typedef _Diff distance_type; // retained

typedef _Pointer pointer;

typedef _Reference reference;

};

// Часть кода опущена

// TEMPLATE CLASS iterator_traits

template<class _Iter>

structiterator_traits

{ // get traits from iterator _Iter

typedef typename _Iter::iterator_category iterator_category;

typedef typename _Iter::value_type value_type;

typedef typename _Iter::difference_type difference_type;

typedef difference_type distance_type; // retained

typedef typename _Iter::pointer pointer;

typedef typename _Iter::reference reference;

};

// iterator_category– категория (тип, тег) итератора

// value_type– класс указываемого объекта

// difference_type– класс для представления разности итераторов

// distance_type– то же самое (для совместимости со старыми версиямиSTL)

// pointer– указатель на указываемый объект

// reference- ссылка

// Далее:

template<class _Ty>

structiterator_traits<_Ty *>

{ // Класс характеристик итераторов для встроенных указателей

// вспомним, что встроенные указатели имеют все свойства

// итераторов произвольного доступа

typedef random_access_iterator_tag iterator_category;

typedef _Ty value_type;

typedef ptrdiff_t difference_type;

typedef ptrdiff_t distance_type; // retained

typedef _Ty *pointer;

typedef _Ty& reference;

};

template<class _Ty>

struct iterator_traits<const _Ty *>

{ // Класс характеристик итераторов для встроенных константный указателей

// вспомним, что встроенные указатели имеют все свойства

// итераторов произвольного доступа

typedef random_access_iterator_tag iterator_category;

typedef _Ty value_type;

typedef ptrdiff_t difference_type;

typedef ptrdiff_t distance_type; // retained

typedef const _Ty *pointer;

typedef const _Ty& reference;

};

О том, зачем нужны такие сложные с виду конструкции – см. чуть далее.

Все STL-итераторы наследуются от классаiterator, поэтому по имеющемуся итератору классаXможно получить его категорию:X::iterator_categoryи т.п.

Кстати, можно заметить в определениях xxx_iterator_tagв явном виде описанную в части «Классификация итераторов» иерархию.

Пример кода: Попробуем создать максимально универсальную функцию, которая суммирует каждый n-й элемент контейнера. Сначала код, потом комментарии.

#include <vector>

#include <list>

#include <iostream>

#include <iterator>

using namespace std;

template<class iter>

voidsumEachNth(iter start, iter end,

typename iterator_traits<iter>::difference_type n,

typename iterator_traits<iter>::value_type &startValue) {

return _sumEachNth(start,end,n,startValue,

iterator_traits<iter>::iterator_category());

}

template<class iter>

void_sumEachNth(iter start, iter end,

typename iterator_traits<iter>::difference_type n,

typename iterator_traits<iter>::value_type &startValue,

random_access_iterator_tag)

{

iter i;

for(i=start; i<end; i+=n) startValue+=*i;

}

template<class iter>

void_sumEachNth(iter start, iter end,

typename iterator_traits<iter>::difference_type n,

typename iterator_traits<iter>::value_type &startValue,

input_iterator_tag)

{

iterator_traits<iter>::difference_type zero(0);

iterator_traits<iter>::difference_type i=zero;

iter it;

for(it=start; it!=end; ++it, ++i) {

if(i==n) {

i=zero;

}

if(i==zero) startValue+=*it;

}

}

intmain() {

int a[7]={1,2,3,4,5,6,7};

list<int> l(a,a+7);

int s=0;

sumEachNth(a,a+7,3,s); // (1)

cout<<"Sum="<<s<<"\n";

s=0;

sumEachNth(l.begin(),l.end(),3,s); // (2)

cout<<"Sum="<<s<<"\n";

return 0;

}

Вывод программы:

Sum=12

Sum=12

Тут используется сразу 2 очень интересных и распространенных трюка. Присутствует 2 реализации функции _sumEachNth: одна – менее эффективная – для произвольного типа итераторов ввода, вторая – более эффективная – для итераторов произвольного доступа. У этих функций разный тип последнего параметра, поэтому это – разные функции для компилятора.

Посмотрим, что делает компилятор в строках (1) и (2).

В строке (1) вызывается sumEachNth для типа итератораint*. Согласно определения из<xutility>

template<class_Ty>

structiterator_traits<_Ty *>

{ // get traits from pointer

typedef random_access_iterator_tag iterator_category;

typedef _Ty value_type;

typedef ptrdiff_t difference_type;

// ...

};

Следовательно iterator_traits<int*>::iterator_category == random_access_iterator_tag, т.е. int* работает как итератор произвольного доступа. Следовательно, в этом случае будет выбрана первая реализация_sumEachNth, принимающая в качестве последнего параметраrandom_access_iterator_tag.

template<class _Iter>

structiterator_traits

{ // get traits from iterator _Iter

typedef typename _Iter::iterator_category iterator_category;

typedef typename _Iter::value_type value_type;

typedef typename _Iter::difference_type difference_type;

typedef difference_type distance_type; // retained

typedef typename _Iter::pointer pointer;

typedef typename _Iter::reference reference;

};

Для списка же категория итератора – list<int>::iterator::iterator_category==(из файла <list>, не будем приводить код)bidirectional_iterator_tag, который может быть приведен кinput_iterator_tag(так как он унаследован от него) и будет выбрана вторая реализация.

Мощь шаблонов С++ в основном и состоит в частичной специализации и мощной системе поиска оптимального соответствия типов параметров функций. Именно благодаря этим двум вещам удалось создать различные характеристики (traits) для итераторов, являющихся указателями и для обычных итераторов, а также создать разные реализации функции для разных типов итераторов без лишних проблем. Не будь их, мы бы не смогли использовать в функцииsumEachNth простые указатели типаint*, поскольку это – не объекты и для них, в отличие отSTL-итераторов, не определена явноiterator_category.

Соседние файлы в папке lab4-iterators