6. Компиляция этого кода приведет к следующему сообщению об ошибке (рис. 3.3). Вы увидите ошибку, связанную с std::iterator_traits
. Подробнее об этом вы узнаете позже. Может случиться так, что вы увидите другую ошибку, если применяете другие компиляторы и/или реализацию библиотеки STL; а возможно даже, что ошибок не будет. Сообщение об ошибке, показанное на рис. 3.3, появляется при использовании компилятора clang version 5.0.0 (trunk 299766).
7. Для исправления ситуации активизируем возможности типажей итераторов для нашего класса итератора. Сразу после определения num_iterator
укажем следующую спецификацию шаблона структуры для типа std::iterator_traits
. Она сообщает STL, что наш итератор num_iterator является однонаправленным и итерирует по целочисленным значениям:
namespace std {
template <>
struct iterator_traits
using iterator_category = std::forward_iterator_tag;
using value_type = int;
};
}
8. Скомпилируем код снова; мы видим, что он работает! Результат работы функций min/max выглядит следующим образом:
100 — 109
Как это работает
Одним алгоритмам STL нужно знать характеристики типа итератора, применяемого вместе с ними, другим — тип элементов, среди которых выполняется перебор. От этого зависят варианты реализации алгоритмов.
Однако все алгоритмы STL будут получать эту информацию о типе с помощью std::iterator_traits
, предполагая, что итератор имеет тип my_iterator
. Этот класс типажа содержит до пяти разных определений членов.
□ difference_type
— значение какого типа мы получим в результате выполнения конструкции it1 — it2
?
□ value_type
— какой тип имеет элемент, к которому мы получаем доступ с помощью *it
(обратите внимание: для чистых итераторов вывода этот тип будет void
)?
□ pointer
— к какому типу должен относиться указатель, чтобы иметь возможность указывать на элемент?
□ reference
— какой тип должна иметь ссылка, чтобы она могла работать как полагается?
□ iterator_category:
к какой категории принадлежит итератор?
Определения типов pointer
, reference
и difference_type
не нужны для нашего итератора num_iterator
, поскольку он не итерирует по реальным значениям int
, но они не доступны на постоянной основе, как это было бы в случае использования массива). Поэтому лучше не определять их, поскольку если алгоритм зависит от доступности элементов диапазона в памяти, то при работе с нашим итератором он может генерировать
Дополнительная информация
До появления C++17 поощрялось наследование итераторами типа std::iterator<...>
, что автоматически заполняет наш класс всеми описаниями типов. Этот механизм все еще работает, но, начиная с С++17, больше не рекомендуется.
Используем оболочки итераторов для заполнения обобщенных структур данных
Часто требуется заполнить некий контейнер большим количеством данных, но источник данных и контейнер
Задачу по перемещению данных между концептуально разными структурами можно решить с помощью одной строки кода благодаря абстракциям, предоставляемым
Как это делается
В этом примере мы используем оболочки для итераторов, просто чтобы продемонстрировать их наличие и способы, которые могут помочь в решении часто встречающихся задач.
1. Сначала включим необходимые заголовочные файлы.
#include
#include
#include
#include
#include
2. Объявим об использовании пространства имен std
, чтобы сэкономить время.
using namespace std;
3. Начнем с итератора std::istream_iterator
. Мы специализируем его для типа int
. Таким образом, он станет преобразовывать данные из стандартного потока ввода в целые числа. Например, при итерировании по нему он будет выглядеть так, будто имеет тип std::vector
. Конечный итератор имеет тот же тип, но не принимает аргументы конструктора.
int main()
{
istream_iterator
istream_iterator