В прошлом видео мы познакомились со списком и узнали, что он позволяет эффективно удалять из середины. В частности, мы это продемонстрировали benchmark'ом, который сравнивает вектор и список. Давайте же на том же самом примере продемонстрируем, что в случае удаления разных элементов из списка у нас не инвалидируются не то что ссылки и указатели, а даже итераторы. Вот возьмём наш пример с нашими контейнерами, NumbersOnVector, NumbersOnList. У нас был benchmark, который показывал, что в случае, когда мы удаляем по десять элементов за 1 000 шагов, список быстрее, чем вектор. Давайте я дополню наш класс только для списка, на самом деле, ещё одним методом, который будет находить последний элемент, удовлетворяющий некоторым условиям. Он тоже будет шаблонным по предикату. Тоже будет принимать один параметр Predicate. Как нам найти последний элемент, который удовлетворяет некоторым условиям? Нам нужно вызвать функцию find, и если мы хотим найти последний, то нам надо искать с конца. То есть здесь нам поможет обратный итератор. Ищем от начала до конца, при этом, на самом деле, начало это конец, а конец это начало, потому что итераторы обратные. Ищем по условию. Зачем же я это делаю? Я хочу перед серией удалений из списка найти последний элемент. [БЕЗ_ЗВУКА] Давайте найдём последний элемент, который делится на REMOVAL_COUNT. Я передаю лямбда-функцию, которая говорит, что ей нужно число X, которое даёт остаток ноль при делении на REMOVAL_COUNT. Вот как-то так. Давайте теперь я не буду удалять всё из контейнера, а оставлю там те элементы, которые делятся на REMOVAL_COUNT, так, чтобы у меня элемент, который здесь мы нашли, остался в контейнере. Давайте я буду итерироваться от единицы. benchmark с вектором я спрячу. [БЕЗ_ЗВУКА] А теперь давайте в конце я посмотрю на этот элемент, который мы нашли перед серией удалений. Давайте я попробую скомпилировать этот код. Код не компилируется. Он жалуется на то, что у нас не работает оператор равно. Вот мы написали лямбда-функцию. Лямбда-функция принимает x и вычисляет остаток от деления x на REMOVAL_COUNT, затем сравнивает его с нулём. [БЕЗ_ЗВУКА] Здесь же мы вызываем функцию find, передавая ей predicate. И не правы мы именно в том, что нам нужна функция не find, а find if. [БЕЗ_ЗВУКА] Компилятор огорчился от того, что мы заставили его искать лямбду в контейнере из чисел. Действительно, странное занятие. Давайте скомпилируем код. Итак, 9 000. Мы добавляли числа от нуля до 10 000, не включительно, и искали последнее, которое делится на тысячу, это число 9 000. Хорошо, значит у нас итератор по крайней мере указывает на тот же самый элемент. А можем ли мы с помощью него проитерироваться до начала списка? Давайте рискнём. Давайте прямо напишем цикл. У нас первый элемент списка это ноль, и он там и остался. Поэтому давайте у нас будет такое условие: пока итератор не указывает на ноль, мы выводим элемент, который под ним лежит и двигаем итератор на одну позицию дальше. Это всё-таки обратный итератор, поэтому будем двигаться к началу. Давайте посмотрим, получится ли у нас проитерироваться этим итератором. Да, у нас получилось. Смотрите, мы вывели все числа от 9 000 до 1 000, с шагом 1 000. Всё хорошо, мы вывели список целиком с помощью итератора, который мы создали ещё тогда, когда мы из списка не поудаляли много разных элементов. Это лишний раз доказывает, что списки страхуют нас от инвалидации и при удалении с помощью метода remove if просто перевешивают указатели, не перемещая сами элементы.