Лямбда-выражения — одна из самых популярных фич современного C++. С тех пор, как они были представлены в C++11, лямбды проникли практически в каждую кодовую базу на C++. И с момента их появления в C++11 их не переставали развивать, добавляя серьезные фичи для работы с ними. Некоторые из этих фич помогают писать более выразительный код, и, поскольку использование лямбда-выражений стало таким распространенным, каждому из нас определенно стоит потратить немного времени на изучение того, что мы можем с ними делать. Цель этой статьи — рассказать об основных эволюционных этапах в истории лямбда-выражений, опустив некоторые мелкие детали. Всесторонний обзор лямбда-выражений уже больше тянет на отдельную книгу, нежели небольшую статью. Если вы хотите узнать больше, я рекомендую вам почитать книгу Бартоломея Филипика (Bartłomiej Filipek) C++ Lambda Story, которая раскрывает эту тему целиком и полностью. Общую эволюцию лямбда-выражений можно охарактеризовать как наделение их возможностями объектов-функций, определяемых вручную. Эта статья требует от вас наличие базовых знаний о лямбда-выражениях C++11. Ну что ж, начнем с C++14. В C++14 лямбда-выражения получили четыре серьезных усовершенствования: параметры по умолчанию; шаблонные параметры; обобщенный захват; возврат лямбды из функции. Начиная с C++14 лямбда-выражения могут принимать параметры по умолчанию, как и любая другая функция: Этот код выводит следующее: В C++11 мы должны определить тип параметров лямбда-выражений: Начиная с C++14 мы можем заставить их принимать любой тип: Даже если вам не нужно обрабатывать несколько типов, эта фича может быть очень полезной, чтобы избежать повторений и сделать код более компактным и читабельным. Например, такая лямбда: становится такой: В C++11 лямбда-выражения могут захватывать только существующие в их области видимости объекты: Но с новым обобщенным лямбда-захватом мы можем инициализировать захватываемые значения практически чем угодно. Вот простой пример: Этот код выводит следующее: Лямбда-выражения приобрели кое-что для себя и благодаря другой фиче C++14: возможности возвращать auto из функции без указания возвращаемого типа. Поскольку тип лямбды генерируется компилятором, в C++11 мы не могли вернуть лямбду из функции: В C++14 мы можем вернуть лямбду, используя auto в качестве типа возвращаемого значения. Это полезно в случаях больших лямбд, находящихся прямо посреди других фрагментов кода: Мы можем обернуть лямбду в другую функцию, тем самым введя новый уровень абстракции: Чтобы узнать больше об этом методе, советую вам почитать о внешних лямбда-выражениях. C++17 привнес очень существенное улучшение в лямбда-выражения: их можно объявлять constexpr: Затем такие лямбды можно использовать в контекстах, оцениваемых во время компиляции: Это особенно полезно при работе с шаблонами. Однако следует отметить, что constexpr лямбды становятся гораздо более полезными в C++20. Действительно, только в C++20 std::vector и большинство алгоритмов STL также становятся constexpr, и их можно использовать с constexpr лямбдами для создания сложных манипуляций с коллекциями, оцениваемыми во время компиляции. Однако есть одно исключение - контейнер std::array. Неизменяющие операции доступа std::array становятся constexpr в C++14, а изменяющие - в C++17. Еще одна фича, которую лямбда-выражения получили в C++17, — это простой синтаксис для захвата копии *this. Рассмотрим следующий пример: Эта лямбда захватывает копию this (указателя). Это может вызвать ошибки памяти, если лямбда переживет объект, например, как в следующем примере: Поскольку MyType уничтожается в конце первого выражения, вызов лямбды во втором операторе разыменовывает this для доступа к его m_value, а он указывает на уже уничтоженный объект. Это приводит к неопределенному поведению (обычно к крашу приложения). Один из возможных способов решить эту проблему — захватить копию всего объекта внутри лямбды. C++17 предоставляет для этого следующий синтаксис (обратите внимание на * перед this): Обратите внимание, что уже в C++14 можно было добиться такого же результата с помощью обобщенного захвата: C++17 только улучшает этот синтаксис. Лямбды продолжили свою эволюцию и в C++20, но на этот раз получили менее фундаментальные фичи, чем в C++14 или C++17. Одним из усовершенствований лямбда-выражений в C++20, которое еще больше приближает их к объектам функций, определяемым вручную, является классический синтаксис для определения шаблонов: Это упрощает доступ к типу шаблонного параметра по сравнению с шаблонными лямбда-выражениями C++14, в которых использовались такие выражения, как auto&&. Другим улучшением является возможность захвата вариативного (variadic) пакета параметров: Мы рассмотрели то, что я считаю основными улучшениями лямбда-выражений от C++14 до C++20. Но это еще не все. Эти важные фичи идут в сопровождении ряда небольших улучшений, которые упрощают написание лямбда-кода. Более глубокое погружение в лямбда-выражения — это отличная возможность лучше понять язык C++, и я думаю, что это стоящая инвестиция времени. Чтобы пойти дальше, лучший известный мне ресурс — это книга Бартоломея Филипика C++ Lambda Story, которую я уже рекомендовал вам. Перевод статьи подготовлен в преддверии старта специализации "C++ Developer".  Редактор