[БЕЗ_ЗВУКА] В прошлом видео мы увидели алгоритмы, которые умеют возвращать набор элементов с помощью итератора, а именно copy_if и set_intersection. Они принимали итератор, в который писали значение. Однако нам для них приходилось заранее готовить вектор, от которого потом вызывать erase, и это было неудобно. Давайте рассмотрим более удобный способ вызывать такие алгоритмы. Мы подключим модуль iterator и затем... Вот у меня есть вектор langs, я хочу из него, допустим, скопировать все языки, которые начинаются на букву C. Я приготовлю вектор. Это все-таки необходимо сделать, если я хочу куда-то сохранить, c_langs. О размере этого вектора я уже могу не думать. Вызываю copy_if от начала диапазона, конца диапазона, здесь — куда я буду сохранять, об этом я скажу чуть позже. И лямбда-функцию передаю, которая проверяет, что первая буква — это C. Вот я снова ее пишу. Проверил, что первая буква — заглавная буква C. И теперь куда же у меня copy_if будет сохранять? Я передам сюда специальный итератор, который по сути, когда в него copy_if будет что-то писать, он будет не идти по готовому контейнеру, а делать pushback в наш контейнер. Что это за итератор, как я его получу? Я вызову функцию back_inserter от c_langs. Как следует из названия, этот итератор нужен для того, чтобы вставлять в конец. Давайте я попробую теперь вывести полученный вектор, c_langs. Посмотрю, не будет ли в нем мусора, будет ли в нем все то, что нужно. Компилирую. Запускаю и вижу языки на букву C. Итак, мы всего лишь создали пустой вектор, back_inserter от него передали в качестве итератора, в который я хочу записывать результат алгоритма copy_if, и у меня вектор после этого готов. Он имеет нужный размер, я заранее не думаю о размере. Некоторые из вас могут вспомнить, что я до этого говорил, что итераторы ничего не знают о контейнере, к которому они относятся, и не могут влиять на его размер, а тут у нас итератор как раз влияет на размер вектора напрямую, то есть он делает pushback в этот контейнер. Ну, на самом деле в начале я сказал не совсем правду, а полная правда следующая: итератор умеет делать лишь ограниченный набор действий. Во-первых, это звездочка — сослаться на конкретный элемент. Можно сослаться на конкретный элемент и туда что-то записать — * итератор = и то, что мы туда записываем. Можно сделать ++ и --. Ну можно еще сравнить итераторы друг с другом. И все. Это все, что они умеют делать. Если у меня есть обычные итераторы begin и end на какой-то контейнер, то получается, все, что я могу делать — это просто обновлять данные, которые по ним лежат, и эти итераторы двигать. Понятно, что я так не могу никаких erase-ов делать, никаких insert-ов, ничего такого. А это итератор back_inserter — специальный итератор. Это итератор, в который если алгоритм делал с него * = и что-то присваивает, этот итератор делает pushback в тот контейнер, к которому он относится. То есть он нужен специально для того, чтобы делать pushback, и больше ни для чего. Аналогично, если я говорю про алгоритм set_intersection, вспоминаю множество чисел, которые у меня были — {1, 3, 8} — и второе множество, например, {3, 7, 8}. Если я хочу их пересечь, наверное, я хочу пересечение множеств сохранить тоже в какое-то множество. У множества, понятно, не будет back_inserter, потому что у него нет pushback-а. Но у множества есть просто метод insert. Поэтому я готовлю множество с результатом, давайте я назову его res, вызываю алгоритм set_intersection, передаю в него все также (begin(a), end(a), begin(b), end(b)) — диапазоны, которые я пересекаю — и передаю, куда мне сохранять результат. Результат мне нужно сохранять в итератор, который будет делать insert в мое множество res, поэтому этот итератор называется inserter. И сюда я передаю, собственно, множество res. Но такой код пока что не скомпилируется, потому что inserter принимает еще один аргумент, это по какому итератору вставлять. Вы помните, что у вектора тоже был метод insert, и он принимал итератор. Он не мог вставить просто так вектор, он вставлял в какую-то конкретную позицию. Поэтому inserter для универсальности всегда принимает итератор. Если вы хотите вставлять во множество, например, можете передать end. Это значения не имеет. end(res). Итак, давайте попробуем код скомпилировать. [БЕЗ_ЗВУКА] Я опечатался в названии функции, сейчас исправлюсь — intersection. И давайте я выведу то, что у меня получится. PrintRange(begin(res), (end(res)); Скомпилирую, запущу и увижу 3, 8. Старый вывод можно закомментировать. Итак, у меня получилось множество из тройки и восьмерки. inserter позволяет вставлять во множество, возвращает специальный итератор, который по сути делает insert во множество. Очень удобно.