Лямбда-выражения

Последнее обновление: 25.03.2023

Лямбда-выражения представляют более краткий компактный синтаксис для определения объектов-функций. Формальный синтаксис лямбда-выражения:

[] (параметры) { действия }

Лямбда-выражение начинается с квадратных скобок. Затем, как в обычной функции, в круглых скобках идет перечисление параметров - типы и их имена. Также начиная со стандарта C++14 для параметров можно указывать значения по умолчанию. За списком параметров, как и в обычной функции, в фигурных скобках помещаются действия, выполняемые лямбда-выражением.

Например, простейшее лямбда-выражение:

[]() { std::cout << "Hello" << std::endl; }

Здесь лямбда выражение не имеет параметров, поэтому указаны пустые скобки. Внутри лямбда-выражения просто выводится на консоль строка "Hello".

Каждый раз, когда компилятор встречает лямбда-выражение, он генерирует новый тип класса, который представляет объект-функцию. В примере выше сгенерированный класс упрощенно может выглядеть примерно так:

class __Lambda1234
{
public:
	auto operator()() const { std::cout << "Hello" << std::endl; }
};

Такой класс имеет произвольное, но уникальное сгенерированное имя. А действия лямбда-выражения определяются в виде оператора (), причем вместо возвращаемого типа применяется слово auto. То есть компилятор сам выводит возвращаемый тип, который может быть void, а может представлять какой-то определенный тип.

Для лямбда-функций без параметров вы можете опустить пустой список параметров (). То есть лямбда-выражение формы []() {...} может быть дополнительно сокращено до [] {...}:

[]{ std::cout << "Hello" << std::endl; }

Вызов лямбда-выражения

Мы можем непосредственно при определении сразу же вызвать лямбда-выражения, указав после тела выражения круглые скобки со значениями для параметров лямбды:

#include <iostream>
 
int main()
{
    [](){std::cout << "Hello" << std::endl;} ();
    // или так
    []{std::cout << "Hello" << std::endl;} ();
}

Поскольку лямбда-выражения здесь не имеют параметров, то для вызова сразу после определения лямбды указываются пустые скобки

[](){std::cout << "Hello" << std::endl;} ();

Это приведет к тому, что будут выполняться действия лямбда-выражения, и на консоль будет выведена строка "Hello".

Именнованные лямбда-выражения

Лямбда-выражение можно определить как переменную:

#include <iostream>
 
int main()
{
    // переменная hello представляет лямбда-выражение 
    auto hello { [](){std::cout << "Hello" << std::endl;} };

    // через переменную вызываем лямбда-выражение
    hello();    // Hello
    hello();    // Hello
}

Здесь переменная hello в качестве значения хранит лямбда-выражение. Чтобы компилятор автоматически определил тип переменной, она определена с ключевым словом auto

Далее через имя переменной мы можем вызвать лямбда-выражение как обычную функцию:

hello();

Параметры

Теперь определим лямбда-выражение с двумя параметрами:

#include <iostream>
 
int main()
{
    auto print { [](const std::string& text){std::cout << text << std::endl;} };

    // вызываем лямбда-выражение
    print("Hello World!");			// Hello World!
    print("Good bye, World...");	// Good bye, World...
}

Здесь лямбда-выражение принимает один параметр типа const std::string&, то есть строку, которая выводится на консоль. И это лямбда-выражение присвоено переменной print.

Вызывая print как стандартную функцию, нужно передать в нее некоторую строку:

print("Hello World!");			// Hello World!
print("Good bye, World...");	// Good bye, World...

Также можно сразу же при определении вызвать лямбда-выражение, передав в него строку

[](const std::string& text){std::cout << text << std::endl;} ("Hell");

Возвращение значения

Лямбда-выражение может возвращать произвольное значение. В этом случае, как и в обычной функции, применяется оператор return:

#include <iostream>
 
int main()
{
    auto sum { [](int a, int b){return a + b;} };

    // вызываем лямбда-выражение
    std::cout << sum(10, 23) << std::endl;  // 33

    // присваиваем его результат переменной
    int result { sum(1, 4)};
    std::cout << result << std::endl;       // 5
}

В данном случае лямбда-выражение возвращает сумму параметров в виде значения int. Соответственно результат выражения мы можем использовать как значение типа int.

Стоит отметить, что по умолчанию компилятор сам определяет, значение какого именно типа будет возвращаться из лямбды. Однако мы также можем явным образом указать возвращаемый тип:

#include <iostream>
 
int main()
{
    auto sum { [](int a, int b) -> double {return a + b;} };

    // вызываем лямбда-выражение
    std::cout << sum(10, 23) << std::endl;  // 33
}

Для установки возвращаемого типа после списка параметров указывается стрелка и собственно возвращаемый тип. Так, в данном случае возвращается значение типа double:

[](int a, int b) -> double {return a + b;} 

То есть сумма чисел, которая по умолчанию представляет тип int, будет преобразована в значение типа double.

Лямбда-выражения как параметры функций

Лямбда-выражение может передаваться в качестве значения параметру функции, который представляет указатель на функцию:

#include <iostream>
 
void do_operation(int a, int b, int (*op)(int, int))
{
    std::cout << op(a, b) << std::endl; 
}

int main()
{
    auto sum { [](int a, int b) {return a + b;} };
    auto subtract { [](int a, int b) {return a - b;} };

    do_operation(10, 4, sum);           // 14
    do_operation(10, 4, subtract);     // 6
}

Здесь определена функция do_operation, которая принимает два числа и указатель на функцию - операцию над этими числами. На место указателя на функцию мы можем передать лямбда-выражение, которое соответствует этому указателю. Это возможно благодаря тому, что компилятор может автоматически сгенерировать для лямбда-выражения оператор преобразования типа в эквивалентный тип указателя функции. Следует отметить, что оператор преобразования не генерируется, если лямбда-выражения обращается к внешним переменным, и далее мы рассмотрим подобную ситуацию.

Также можно определить лямбда-выражение непосредственно при использовании, что может быть полезно, если лямбду больше нигде не планируется использовать:

do_operation(10, 4, [](int a, int b) {return a * b;});  // 40

Универсальные лямбда-выражения

Универсальное лямбда-выражение (generic lambda) — это лямбда-выражение, в котором как минимум для одного параметра в качестве типа указано слово auto или выражения auto& или const auto&. Это позволяет уйти от жесткой привязки параметров к определенному типу. Например:

#include <iostream>

int main()
{
    auto add = [](auto a, auto b) {return a + b;};
    //auto print = [](const auto& value) {std::cout << value << std::endl; };

    std::cout << add(2, 3) << std::endl;        // 5 - складываем числа int
    std::cout << add(2.2, 3.4) << std::endl;    // 5.6 - складываем числа double

    std::string hello{"hello "};
    std::string world{"world"};
    std::cout << add(hello, world) << std::endl;    // hello world - складываем строки
}

В данном случае определено лямбда-выражение, которое принимает два параметра и возвращает их сумму. Оно присвоено переменной add:

auto add = [](auto a, auto b) {return a + b;};

То есть на момент написания мы не знаем, какие типы будут представлять параметры. Конкретные типы будет выводить компилятор при вызове лямбда-выражения исходя из переданных в него значений:

std::cout << add(2, 3) << std::endl;

Так, в данном случае передаются два числа типа int, соответственно результат будет сумма этих чисел в виде значения int.

Другой пример - определим универсальное лямбда-выражение, которое выводит произвольное значение на консоль:

#include <iostream>

int main()
{
    auto print = [](const auto& value) {std::cout << value << std::endl; };

    print("Hello");
    print(4);
    print(45.6789);
}
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850