Перейти к содержимому
Теория · Функции · урок 11 из 13

Лямбды: первое знакомство

Определить функцию внутри другой функции нельзя: int twice(int x) { return x * 2; } внутри main компилятор не примет. Но короткая функция часто нужна в одном месте, и выносить её из main неудобно. Такую функцию можно записать лямбдой прямо там, где она нужна. Это как записка на стикере, приклеенная к месту работы, вместо отдельной инструкции в шкафу:

C++
#include <iostream>

int main() {
    auto twice = [](int x) { return x * 2; };
    std::cout << twice(21) << '\n';  // 42
}

Устройство лямбды

Лямбда — функция без имени, записанная прямо внутри выражения. Лямбда состоит из трёх частей:

  • квадратные скобки [] начинают лямбду;
  • в круглых скобках стоят параметры;
  • в фигурных скобках стоит тело.

Тип результата компилятор выводит из return, как тип auto-переменной — из её значения. В примере x * 2 имеет тип int, поэтому и лямбда возвращает int.

Лямбду сохраняют в переменную, объявленную через auto: тип у лямбды есть, но назвать его в коде нельзя. Каждая лямбда получает свой тип, который придумывает компилятор. Вызывают лямбду по имени переменной, как обычную функцию: twice(21). Параметры лямбды, как и параметры функции, передают по значению или по ссылке.

Захват переменных

Чтобы лямбда могла использовать локальную переменную step из main, переменную нужно захватить. Захват — список переменных, которые лямбда берёт из окружающего кода; его пишут в квадратных скобках. Захватить переменную можно двумя способами:

  • [step] копирует step в момент создания лямбды, как фотография доски: что было написано тогда, то и останется на снимке;
  • [&step] даёт лямбде ссылку на саму step, как окно, через которое доску видно всегда:
C++
#include <iostream>

int main() {
    int step = 10;
    auto add_copy = [step](int x) { return x + step; };
    auto add_ref = [&step](int x) { return x + step; };
    step = 100;
    std::cout << add_copy(1) << ' ' << add_ref(1) << '\n';  // 11 101
}

add_copy хранит копию со значением 10: она сделана, когда выполнялась строка с лямбдой, поэтому изменение step до неё не доходит. add_ref читает саму step, которая к моменту вызова уже равна 100.

Через захват по ссылке лямбда может и менять переменную: [&count]() { ++count; } при каждом вызове увеличивает count из main. Копию из [step] лямбда может только читать: [step]() { ++step; } не скомпилируется. Это тот же выбор, что у параметров функции: копия или другое имя той же переменной. Несколько переменных перечисляют через запятую: [a, &b].

Частая ошибка

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

C++
int main() {
    int step = 10;
    auto add = [](int x) { return x + step; };  // ✗
    return add(1);
}
TEXT
error: variable 'step' cannot be implicitly captured in a lambda with no capture-default specified

Это ошибка компилятора: пустые скобки [] значат, что лямбда ничего не захватывает. Добавьте переменную в захват — [step] или [&step].

Главное

  • Лямбда — функция без имени: [] начинают её, в () стоят параметры, в {} — тело.
  • Лямбду хранят в auto-переменной и вызывают по её имени, как обычную функцию.
  • [step] копирует переменную при создании лямбды, а [&step] читает саму переменную при каждом вызове.
  • Переменная, которую лямбда использует, но не захватила, даёт ошибку компилятора.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом