Давайте продолжим работать с примером, в котором мы создаем сортированный вектор, и давайте представим, что нам нужно сделать так, чтобы он был не только отсортирован, но еще чтобы в нем отсутствовали дубликаты, то есть чтобы он был уникализирован. Как мы можем это сделать? Ну первый вариант такой. Мы можем написать функцию Unique по аналогии с функцией Sorted, то есть пишем функцию Unique, она тоже принимает вектор по значению, но здесь она вызывает unique auto it = unique begin (data), end(data). Дальше мы делаем data.erase (it, end (data) и возвращаем вектор data. И когда мы конструируем наш вектор sorted numbers, мы вызываем Unique от Sorted. Вполне себе подходящий рабочий хороший вариант. Мы вызываем одну функцию и передаем объект в другую и потом инициализируем константный вектор. Но у такого подхода может быть ряд недостатков. Например, если вот эта вот дополнительная инициализация, она какая-то уникальная и нужна только в одном месте, то делать для нее отдельную функцию, которую мы помещаем куда-то в глобальное пространство имен, может быть не самым подходящим решением, потому что мы и название для этой функции должны придумать, и мы тем самым засоряем глобальное пространство имен. Поэтому иногда это создание такой дополнительной функции может быть неудобным. Кроме того, когда мы читаем вот этот вот код, нам может захотеться посмотреть, а что же делает, собственно, функция Unique. И если это какая-то опять же нетривиальная и единоразовая инициализация, то иногда лучше иметь ее код рядом с тем местом, где мы используем, а не выносить куда-то в другое место. Таким образом, мы можем захотеть попробовать каким-то другим образом выполнить инициализацию нашего вектора. Ну как мы можем это сделать? Мы можем, например, снять с него константность и вот эти манипуляции проделать следующей командой. Вот мы создали вектор sorted_numbers, и уникализируем его мы следующей командой. Это у нас не компилируется, потому что мы не все исправили. Так это у нас компилируется, но нам пришлось снять константность с вектора sorted_numbers, хотя мы только в предыдущем видео подробно разобрали, насколько она полезна и насколько она необходима в этом самом месте. Поэтому такой вариант инициализации нам особо не подходит. Нам нужен какой-то другой способ оставить вектор константным, но при этом выполнить какую-то нетривиальную инициализацию рядом с его объявлением. И именно для этого в C++ есть специальная идиома, которая называется immediately invoked lambda expression. Суть ее в том, что для инициализации константного объекта мы создаем lambda-функцию и тут же, рядом с тем местом, где мы ее создали, мы ее вызываем. Вот вы можете видеть, у меня на экране увеличились круглые скобки, чтобы подчеркнуть, что мы создали lambda-функцию и тут же ее вызвали. И в примере на слайде мы делаем следующее: мы считываем целое число из входного потока и возвращаем его гарантированно неотрицательным. Таким образом, вызывая вот эту вот лямбду в месте ее создания, мы можем не терять константность при объявлении переменной non-negative value. И давайте применим вот эту идиому IILE в нашем примере. Выглядеть это будет таким образом. Мы возвращаем константность у вектора sorted_numbers, пишем равно, здесь пишем фигурные скобочки, дальше объявляем вектор целых чисел data и ему присваиваем результат вызова функции Sorted. Дальше, ну проще на самом деле вот это все отсюда скопировать, мы этот вектор data уникализируем, очищаем дубли и возвращаем. И вот у нас есть вот здесь фигурная скобка, мы после нее ставим круглые скобки, говоря, что мы эту лямбду вызываем и ставим точку с запятой. Удаляем код, который у нас здесь был, запускаем компиляцию. Так, здесь у нас ругаются на функцию Unique, она нам больше не нужна. И таким образом мы выполняем инициализацию нашего вектора, сначала сортируя входные данные, затем гарантируя, что сам вектор будет уникализирован, и сохраняя его константность. На самом деле идиома IILE бывает очень полезна в случае, когда нам нужно замерить время конструирования объекта. Смотрите, как это делается. Давайте подключим заголовочный файл profile h, который мы разработали в «Красном поясе по C++», и допустим, мы вернемся к ситуации, когда у нас вектор sorted_numbers инициализировался только с сортированными числами, не уникализированными. Так, компилируется? Компилируется. Мы хотим замерить, а сколько же времени у нас уходит на конструирование вот этого вектора, и мы это можем сделать опять же с помощью нашей идиомы. Мы создаем lambda-функцию, делаем ее вызов. Здесь запускаем LOG_DURATION Sorted numbers build, а внутри делаем return Sorted. Все, таким образом мы инициализируем наш вектор с помощью вызова lambda-функции, и внутри этого вызова мы замеряем, сколько работает вызов функции Sorted. Конечно, вы можете подумать, что мы могли бы LOG_DURATION поместить внутрь функции Sorted, но тогда будет замеряться время всех вызовов этой функции, у нас будет засоряться поток ошибок, а здесь мы конкретный вызов, который нас интересует, замеряем. И это делается очень удобно, с помощью идиомы immediately invoked lambda expression. У вас может возникнуть вопрос, а не приводит ли использование вот этой вот лямбды, ее конструирование, вызов, к замедлению кода? Ну на самом деле это я могу вам оставить в качестве домашней работы и предложить самим написать benchmark и убедиться, что идиома IILE никак не сказывается негативно на скорости работы вашей программы. Ну и последний момент, который стоит отметить, говоря об этой идиоме, заключается вот в чем. При объявлении и инициализации переменной с помощью идиомы IILE крайне не рекомендуется использовать auto для указания типа этой переменной. То есть вот как в примере на экране, рекомендуется явно написать, что это вектор int, а не использовать auto. Почему? Давайте представим, что мы все-таки используем auto и мы читаем наш код сверху вниз. Вот мы читаем, читаем, читаем и видим строчку: const auto sorted_numbers равно квадратные скобки, открывающаяся фигурная скобка. Вот мы пока вниз еще не посмотрели, но вот из этой строчки мы можем сделать вывод, что мы сейчас создаем новую lambda-функцию. И эта lambda-функция будет называться sorted_numbers, и мы туда заглядывать внутрь, чтобы посмотреть, что она делает. Но вот эти вот круглые скобки в конце лямбды, которые ее вызывают, они обычно не заметны, и мы можем абсолютно быть уверенными, что вот этот вот код объявляет lambda-функцию. Когда же у нас тип указан явно, то при чтении кода мы видим, что в этой строчке мы объявляем переменную типа вектор int. Дальше мы видим = [ ] и }, и мы понимаем, это идиома immediately invoked lambda expression. Мы создаем лямбду, и сейчас в конце мы ее вызовем, чтобы проинициализировать этот вектор. И таким образом мы не вводим читателя в заблуждение, и он сразу понимает, что здесь не лямбда создается, а здесь создается вектор целых чисел. Давайте подведем итоги. В этом видео мы с вами познакомились с идиомой immediately invoked lambda expression, которая может быть полезна для нетривиальной инициализации константных объектов. При этом она особенно удобна, когда нам хочется замерить время конструирования того или иного объекта. Ну, и как мы сказали в конце, не рекомендуется использовать auto при объявлении константных объектов, которые инициализируются с помощью идиомы IILE.