Итераторы

Тема дорожной карты · Modern C++ Developer

Итераторы — STL-абстракция, обобщающая указатели: объект, который умеет «указывать на элемент контейнера» и «продвигаться к следующему». Любой STL-контейнер (std::vector, std::list, std::map, std::set) и любой STL-алгоритм (std::sort, std::find, std::transform) общаются через итераторы — это позволяет писать generic-код без знания конкретного контейнера. В C++20 итераторы дополнены концепцией ranges, упрощающей композицию.

Как это работает

STL определяет несколько категорий итераторов с разными возможностями: InputIterator (только чтение, продвижение вперёд — istream_iterator), OutputIterator (только запись — back_inserter), ForwardIterator (чтение+запись, многократный проход), BidirectionalIterator (плюс --itlist, set), RandomAccessIterator (плюс it + n за O(1) — vector, deque), ContiguousIterator (C++20, гарантия contiguous-памяти — vector, array, string). Алгоритмы STL требуют минимальную категорию: std::sort требует random-access, std::find — input. C++20 ranges предлагает views (ленивые трансформации) и pipe-syntax: vec | std::views::filter(pred) | std::views::transform(fn).

Когда применять

Используйте итераторы везде, где обрабатываете коллекцию: std::transform вместо ручного цикла, std::accumulate вместо ручного суммирования, custom алгоритмы поверх итераторов работают на любом контейнере. Для своих контейнеров — реализуйте итераторы по STL-конвенции, и они автоматически работают с std::sort, std::copy, range-based for. С C++20 ranges предпочтительны над сырыми итераторами в новом коде.

Типичные ошибки

Инвалидация итераторов — vec.erase(it) инвалидирует it, используйте возвращаемое значение (it = vec.erase(it)). Несоответствие требований — передача list::iterator в std::sort не компилируется (нужен random-access). Сравнение итераторов из разных контейнеров — undefined behavior. Использование auto&& для loop-переменной в range-for, забывая декремент-семантику для proxy-итераторов (vector<bool>). Хранение итераторов между модификациями контейнера — UB. Использование end() после push_back в vector — UB, если произошёл reallocation, нужно перечитать end() заново.

Существуют адаптеры итераторов в <iterator> хедере: std::back_inserter, std::front_inserter, std::inserter оборачивают контейнер в OutputIterator для алгоритмов вроде std::copy. std::reverse_iterator инвертирует направление обхода. std::move_iterator (C++11) — при разыменовании отдаёт rvalue-reference, позволяя std::copy фактически мувить элементы. C++20 добавил std::counted_iterator и std::common_iterator — оба для интероперабельности с ranges.

Связанные понятия

Полезные ресурсы