Что такое lambda-выражения в C++? Как захватывать переменные?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое lambda-выражения в C++? Как захватывать переменные?
Lambda-выражения — это анонимные функции, которые можно определить прямо в месте использования. Они появились в C++11 и существенно упростили код при работе с алгоритмами и колбэками.
Синтаксис lambda
[capture] (parameters) -> return_type { body }
Самый простой пример:
auto add = [](int a, int b) { return a + b; };
int result = add(3, 5); // 8
// С явным типом возврата
auto mul = [](int a, int b) -> int { return a * b; };
// Без параметров
auto hello = []() { std::cout << "Hello\n"; };
hello();
Захват переменных (Capture Clause)
Захват по значению ([=])
int x = 10;
auto func = [=]() { std::cout << x; }; // Копирует x
x = 20;
func(); // Выведет 10, а не 20!
Захват по значению создаёт копию переменной на момент создания lambda:
int multiplier = 5;
auto times = [multiplier](int x) { return x * multiplier; };
multiplier = 10;
std::cout << times(3); // 15, а не 30
Захват по ссылке ([&])
int count = 0;
auto increment = [&]() { count++; };
increment();
increment();
std::cout << count; // 2
При захвате по ссылке lambda видит текущее значение переменной. Опасно! Если переменная уничтожится, ссылка будет висящей (dangling):
std::function<void()> createLambda() {
int x = 10;
return [&]() { std::cout << x; }; // ✗ ОПАСНО!
// x уничтожится при выходе из функции, ссылка станет невалидной
}
Выборочный захват
Захватывай только нужные переменные:
int a = 1, b = 2, c = 3;
// Захвати a и b по значению, остальные по ссылке
auto func = [a, b, &c](int x) {
return a + b + c + x;
};
// Захвати всё по значению, кроме c (по ссылке)
auto func2 = [=, &c]() {
return a + b + c;
};
// Захвати всё по ссылке, кроме a (по значению)
auto func3 = [&, a]() {
return a + c;
};
Модификация захватанных переменных
По умолчанию lambda не может изменять захватанные переменные:
int x = 5;
auto func = [x]() {
x++; // ✗ Ошибка! Нельзя изменять
};
Чтобы разрешить модификацию, добавь mutable:
int x = 5;
auto func = [x]() mutable {
x++; // ✓ Теперь можно
return x;
};
func(); // 6
std::cout << x; // 5 — исходная переменная не изменилась!
При захвате по ссылке, mutable не требуется:
int x = 5;
auto func = [&]() {
x++; // ✓ Работает без mutable
};
func();
std::cout << x; // 6 — изменение видно снаружи
Практические примеры
Сортировка с компаратором
std::vector<int> nums = {3, 1, 4, 1, 5, 9};
// Lambda вместо функтора
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return a > b; }); // По убыванию
Фильтрация в std::find_if
std::vector<int> nums = {1, 2, 3, 4, 5};
auto it = std::find_if(nums.begin(), nums.end(),
[](int x) { return x % 2 == 0; }); // Найди первое чётное
Обработка с сохранением состояния
int sum = 0;
std::vector<int> nums = {1, 2, 3, 4};
std::for_each(nums.begin(), nums.end(),
[&sum](int x) { sum += x; }); // Захвати sum по ссылке
std::cout << sum; // 10
Возврат lambda из функции (C++14+)
auto makeAdder(int n) {
return [n](int x) { return x + n; }; // Захвати n по значению
}
auto add5 = makeAdder(5);
std::cout << add5(10); // 15
Правила для захвата
- По значению ([=]) — когда переменная должна "заморозиться"
- По ссылке ([&]) — когда нужно видеть изменения снаружи
- Выборочно — когда нужна точность
- Избегай [&] в возвращаемых lambdas — риск висящей ссылки
C++14 и далее: обобщённые lambda
// C++14 — параметры можно объявлять как auto
auto sum = [](auto a, auto b) { return a + b; };
std::cout << sum(1, 2); // 3
std::cout << sum(1.5, 2.5); // 4
std::cout << sum("Hello"s, " World"s); // "Hello World"
Итого
Lambda — это мощный инструмент для:
- Компактных функций в алгоритмах
- Колбэков без создания отдельных классов-функторов
- Замыканий с сохранением контекста
Главное правило: всегда чётко понимай, как и что ты захватываешь!