Что такое bidirectional iterator в C++ STL?

«Что такое bidirectional iterator в C++ STL?» — вопрос из категории STL, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Bidirectional iterator (двунаправленный итератор) — это одна из категорий итераторов в C++, которая расширяет возможности forward iterator, добавляя возможность перемещаться не только вперед, но и назад по элементам последовательности.

Ключевые операции, которые он поддерживает (помимо операций forward iterator):

  • Префиксный и постфиксный декремент: --it и it-- (переход к предыдущему элементу).
  • Все операции forward iterator: инкремент (++), разыменование (*), сравнение (==, !=).

Какие контейнеры STL предоставляют двунаправленные итераторы:

  • std::list
  • std::set, std::multiset
  • std::map, std::multimap
  • std::deque (также предоставляет random access iterator)

Пример использования:

#include <list>
#include <iostream>
#include <algorithm>

int main() {
    std::list<int> numbers = {10, 20, 30, 40, 50};

    // 1. Движение вперед и назад
    auto it = numbers.begin();
    std::advance(it, 2); // it указывает на 30
    std::cout << *it << std::endl; // 30

    --it; // Движение назад к 20
    std::cout << *it << std::endl; // 20

    // 2. Обратный итератор (адаптер, построенный на основе двунаправленного)
    for (auto rit = numbers.rbegin(); rit != numbers.rend(); ++rit) {
        std::cout << *rit << ' '; // Выведет: 50 40 30 20 10
    }
    std::cout << std::endl;

    // 3. Алгоритмы, требующие двунаправленных итераторов
    // std::reverse требует двунаправленных итераторов
    std::reverse(numbers.begin(), numbers.end());
    // numbers теперь {50, 40, 30, 20, 10}

    // 4. Многопроходность гарантирована
    auto it1 = numbers.begin();
    auto it2 = numbers.begin(); // Два независимых итератора
    ++it1;
    // it2 по-прежнему указывает на начало, можно снова пройти по контейнеру.

    return 0;
}

Ограничения bidirectional iterator (по сравнению с random access iterator):

  • Нет арифметики с целыми числами: Нельзя it + 5 или it += 3. Для перемещения на N позиций нужно использовать std::advance(it, N) или цикл.
  • Нет операций сравнения <, >, <=, >=. Только == и !=.
  • Нет оператора индексации [].

Практический вывод: Если ваш алгоритм или функция должны работать с контейнерами вроде std::list или std::map, они должны быть написаны с учетом ограничений двунаправленных итераторов, используя только операции ++, --, *, == и !=. Это обеспечивает максимальную универсальность.