Лямбда-функция

Имеет следующий синтаксис:

auto lambda = [](int a, int b) {return a < b;}

Полученный объект по сути является структурой с известным только компилятору уникальным типом, имеющим operator() с аргументами, которые передаются в лямбду.

Возвращаемый тип можно задать явно через trailing return types, либо довериться компилятору (равносильно типу auto).

Квадратные скобки здесь не просто для красоты - они используются для захвата переменных из контекста объявления лямбда-функции:

int a = 42;

// захват переменной по значению
auto mul = [a](int k) {return k * a;}

// захват переменной по ссылке
auto add = [&a](int k) {return k + a;}

// захват переменной по значению с присвоением нового имени (C++14)
auto sub = [b = a](int k) {return k - b;}

// захват переменной по ссылке с присвоением нового имени (C++14)
auto div = [&b = a](int k) {return k / b;}

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

int x, y;

[=](){}    // все по значению
[=, &x](){} // все по значению, x - по ссылке

[&](){}    // все по ссылке
[&, x](){} // все по ссылке, x - по значению

Захваченные по значению объекты являются константными, если лямбда-функция не имеет спецификатора mutable. Также можно захватывать this, как указатель на объект, где лямбда была объявлена, а можно захватывать *this, как копию объекта.

Свойства лямбд

  • могут копироваться и перемещаться
  • не могут присваиваться
  • лямбды без захвата могут конвертиться к указателю на функцию
  • при копировании лямбды копируются все захваченные по значению переменные
  • захват по ссылке не продлевает время жизни объектов. Такой код работает неправильно:
auto foo() {
    std::vector<int> v;
    return [&v]() {
        // ...
    };
} // v уничтожится при выходе из функции, обращение внутри лямбды - UB

Начиная с C++20 лямбды могут иметь шаблонные параметры:

int main() {
    auto less = []<typename T>(T a, T b) {return a < b;};
    bool val = less(42, 43);
}

Рекурсивный вызов лямбды

Пример ниже выдаст ошибку компиляции:

auto factorial = [&factorial](int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
};

error: use of 'factorial' before deduction of 'auto', что говорит нам о том, что тип auto еще не был выведен, поэтому пользоваться им нельзя.

Вариант 1

std::function<int(int)> factorial;

factorial = [&](int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
};

std::cout << factorial(4);

Вариант 2

auto factorial = [](int n, auto&& f) -> int {
    return (n <= 1 ? 1 : n * f(n - 1, f));
};

std::cout << factorial(4, factorial);

Заимствования:

cpp-notes/19_lambdas_type_erasure.md at master · lejabque/cpp-notes (github.com)