Ответ
Bidirectional iterator (двунаправленный итератор) — это одна из категорий итераторов в C++, которая расширяет возможности forward iterator, добавляя возможность перемещаться не только вперед, но и назад по элементам последовательности.
Ключевые операции, которые он поддерживает (помимо операций forward iterator):
- Префиксный и постфиксный декремент:
--itиit--(переход к предыдущему элементу). - Все операции forward iterator: инкремент (
++), разыменование (*), сравнение (==,!=).
Какие контейнеры STL предоставляют двунаправленные итераторы:
std::liststd::set,std::multisetstd::map,std::multimapstd::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, они должны быть написаны с учетом ограничений двунаправленных итераторов, используя только операции ++, --, *, == и !=. Это обеспечивает максимальную универсальность.
Ответ 18+ 🔞
А, ну вот, очередная порция итераторов подъехала. Слушай, это как с машинами: есть просто вперёд, а есть с задней передачей. Так вот bidirectional iterator — это как раз та самая задняя передача, ёпта.
Двунаправленный итератор — это такой тип, который умеет всё то же, что и обычный однонаправленный (forward), но ещё может и пятиться назад, как краб обиженный. Не только ++, но и --, представляешь?
Что он умеет, кроме базового набора:
- Идти назад:
--itиit--. То есть если накосячил и проскочил нужный элемент, можно вернуться, а не ходить по кругу с начала, как дурак. - Всё остальное от предка: шагать вперёд, смотреть на элемент (
*) и сравнивать.
Где такое чудо встечается в STL:
std::list— классика, связный список.std::set,std::multiset— деревья.std::map,std::multimap— тоже деревья, но с ключами.std::deque— он вообще крутой, у него и рандомный доступ есть, но и двунаправленный он тоже даёт, хитрая жопа.
Смотри, как этим пользоваться:
#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); // упёрлись в 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 — его бы без --it не было бы вообще
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;
}
А теперь, блядь, ложка дёгтя. Чем он НЕ умеет (в отличие от random access iterator):
- Арифметики нет, ёпта! Нельзя написать
it + 5. Вообще. Придётся илиstd::advance(it, N)вызывать, или в цикле прыгать, что для списка — пиздец как долго. - Сравнивать на больше/меньше нельзя. Только
==и!=. Не спросишь, который итератор «левее». - Квадратных скобочек
[]тебе тоже не видать. Это не массив, чувак.
Суть в чём: если пишешь универсальный алгоритм, который должен работать и со списками, и с мапами — забудь про it + n и прочие радости жизни. Работай только через ++, --, * и сравнение. Тогда твой код будет летать везде, а не только в векторах да массивах. Иначе будет тебе хиросима, когда попробуешь этот алгоритм к std::list прикрутить.