using namespace std;
3. Чтобы у нас появился объект, по которому мы сможем итерировать, создадим список, содержащий целые числа:
int main()
{
list
4. Теперь выведем эти числа на экран в обратном порядке. Для этого проитерируем по списку благодаря функциям rbegin
и rend
класса std::list
и выведем данные значения с помощью стандартного потока вывода, используя удобный адаптер ostream_iterator
:
copy(l.rbegin(), l.rend(), ostream_iterator
cout << '\n';
5. Если контейнер не предоставляет функций rbegin
и rend
, но хотя бы выдает двунаправленные итераторы, то поможет функция std::make_reverse_iterator
. Она принимает
copy(make_reverse_iterator(end(l)),
make_reverse_iterator(begin(l)),
ostream_iterator
cout << '\n';
}
6. Компиляция и запуск программы дадут следующий результат:
5, 4, 3, 2, 1,
5, 4, 3, 2, 1,
Как это работает
Преобразование обычного итератора в обратный возможно при поддержке двунаправленного перебора. Это требование выполняется любым итератором, который находится в категории
Обратный итератор
Еще одна деталь связана с позициями начала и конца. Взглянем на рис. 3.4, на котором показана стандартная последовательность чисел, хранящаяся в итерабельном диапазоне данных. Если она содержит числа от 1
до 5
, то начальный итератор должен указывать на элемент 1
, а конечный итератор — на элемент, стоящий сразу после 5
.
При определении обратных итераторов итератор rbegin
должен указывать на элемент 5
, а итератор rend
— на элемент, стоящий сразу 1
. Переверните книгу вверх ногами — и убедитесь в том, что это имеет смысл.
Если мы хотим, чтобы наши собственные классы-контейнеры поддерживали обратный перебор, то не нужно реализовывать все эти детали самостоятельно, можно просто обернуть обычные итераторы в обратные с помощью вспомогательной функции std::make_reverse_iterator
, и она сделает всю работу за нас.
Завершение перебора диапазонов данных с использованием ограничителей
Как алгоритмы STL, так и основанные на диапазонах циклы for
предполагают, что начальная и конечная позиции для перебора известны
Самый простой пример такой ситуации — это перебор в стиле С простых строк, длина которых во время
for (const char *c_ponter = some_c_string;
*c_pointer != '\0'; ++c_pointer)
{
const char c = *c_pointer;
// сделаем что-нибудь с переменной c
}
Единственный способ поработать с этими строками в основанном на диапазоне цикле for
заключается в том, чтобы обернуть их в объект std::string
, который поддерживает функции begin()
и end()
:
for (char c : std::string(some_c_string)) { /* сделаем что-нибудь с c */ }
Однако конструктор класса std::string
будет итерировать по всей строке до того, как этим сможет заняться созданный нами цикл. В С++17 появился класс std::string_view
, но его конструктор также один раз проитерирует по всей строке. std::istream_iterator
тоже сталкивается с подобными случаями в момент приема входящих данных из std::cin
, поскольку его конечный итератор не может реалистично указывать на конец потока данных, когда пользователь
Начиная с C++17 начальный и конечный итераторы не обязаны иметь один тип. В данном разделе мы продемонстрируем, как
Как это делается
В этом примере мы создадим итератор и класс диапазона, который позволит проитерировать по строке неизвестной длины, не зная
1. Сначала, как и всегда, включим заголовочные файлы:
#include