Лямбды: первое знакомство
Определить функцию внутри другой функции нельзя: int twice(int x) { return x * 2; } внутри main компилятор не примет. Но короткая функция часто нужна в одном месте, и выносить её из main неудобно. Такую функцию можно записать прямо там, где она нужна, — лямбдой (lambda):
#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, переменную нужно захватить. Захват (capture) — переменные, которые лямбда берёт из окружающего кода; их перечисляют в квадратных скобках. [step] копирует step в момент создания лямбды, а [&step] даёт лямбде ссылку на саму step:
#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].
Частая ошибка
Переменную используют в лямбде, но не захватывают:
int main() {
int step = 10;
auto add = [](int x) { return x + step; }; // ✗
return add(1);
}error: variable 'step' cannot be implicitly captured in a lambda with no capture-default specifiedЭто ошибка компилятора: пустые скобки [] значат, что лямбда ничего не захватывает. Добавьте переменную в захват — [step] или [&step].