← Назад к вопросам

Что такое лямбда?

1.0 Junior🔥 191 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Определение и назначение

Лямбда (lambda) — это безымянная функция (функтор), объявленная и определённая в точке использования. Введены в C++11 и стали одной из ключевых фишек современного C++.

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

Синтаксис лямбды

[capture](parameters) -> ReturnType { body }

Компоненты:

  • [capture] — список переменных, захватываемые из внешней области
  • (parameters) — параметры функции (может быть пусто)
  • -> ReturnType — тип возврата (опционально, выводится автоматически)
  • { body } — тело лямбды

Пример базовой лямбды

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    // Простая лямбда без параметров и захватов
    auto greet = []() { std::cout << "Hello, Lambda!\\n"; };
    greet();  // Вызов: Hello, Lambda!

    // Лямбда с параметрами
    auto add = [](int a, int b) { return a + b; };
    std::cout << add(3, 5) << std::endl;  // 8

    // Лямбда с явным типом возврата
    auto divide = [](double a, double b) -> double { return a / b; };
    std::cout << divide(10.0, 3.0) << std::endl;  // 3.33...

    return 0;
}

Захват переменных (capture)

Это самая мощная часть лямбд — управление доступом к переменным из окружающей области:

1. Захват по значению [=]

int x = 10, y = 20;
auto lambda = [=]() { return x + y; };  // Копируем x и y
std::cout << lambda() << std::endl;  // 30

Изменение x и y снаружи не влияет на лямбду.

2. Захват по ссылке [&]

int x = 10, y = 20;
auto lambda = [&]() { return x + y; };  // Ссылка на x и y
x = 100;  // Изменяем x
std::cout << lambda() << std::endl;  // 120

Лямбда видит новое значение x. Опасно, если лямбда живёт дольше переменной!

3. Смешанный захват [=, &x]

int x = 10, y = 20, z = 30;
auto lambda = [=, &x]() { return x + y + z; };
// Захватили y и z по значению, x по ссылке

x = 100;
std::cout << lambda() << std::endl;  // 100 + 20 + 30 = 150

4. Явный захват [x, y]

int x = 10, y = 20, z = 30;
auto lambda = [x, y]() { return x + y; };  // Только x и y
// z недоступен, это ошибка:
// auto bad = [x, y]() { return x + y + z; };  // ERROR!

5. Инициализирующий захват (C++14) [x = 42, &y]

int original_x = 10;
auto lambda = [x = 100, &y]() { return x + y; };
// x захвачена со значением 100 (не 10!)

Полезно для копирования и переименования переменных.

Использование в алгоритмах STL

Лямбды очень удобны с функциями стандартной библиотеки:

#include <algorithm>
#include <vector>

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};

    // sort с кастомным компаратором
    std::sort(nums.begin(), nums.end(), 
              [](int a, int b) { return a > b; });  // По убыванию

    // find_if с условием
    auto it = std::find_if(nums.begin(), nums.end(),
                           [](int x) { return x > 3; });

    // for_each с побочным эффектом
    int sum = 0;
    std::for_each(nums.begin(), nums.end(),
                  [&sum](int x) { sum += x; });  // Захват sum по ссылке

    std::cout << "Sum: " << sum << std::endl;
    return 0;
}

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

Каждая лямбда имеет уникальный анонимный тип. Вы не можете (легко) узнать этот тип:

auto lambda1 = []() {};
auto lambda2 = []() {};

// lambda1 и lambda2 имеют РАЗНЫЕ типы, даже если выглядят одинаково!

Для хранения лямбд разных типов используйте std::function:

#include <functional>

std::function<int(int, int)> operation;

operation = [](int a, int b) { return a + b; };
std::cout << operation(5, 3) << std::endl;  // 8

operation = [](int a, int b) { return a * b; };
std::cout << operation(5, 3) << std::endl;  // 15

Стационарные лямбды (mutable, C++11)

По умолчанию лямбда захватывает переменные по значению как const — вы не можете менять копию внутри лямбды:

int x = 10;
auto lambda = [x]() { x++; };  // ERROR: x захвачена как const

Решение — добавить mutable:

int x = 10;
auto lambda = [x]() mutable { x++; return x; };
std::cout << lambda() << std::endl;  // 11
std::cout << x << std::endl;         // 10 (оригинал не изменился)

Лямбда с состоянием (C++14+)

Инициализирующий захват позволяет создавать лямбды со своим состоянием:

auto counter = [count = 0]() mutable { return ++count; };
std::cout << counter() << std::endl;  // 1
std::cout << counter() << std::endl;  // 2
std::cout << counter() << std::endl;  // 3

Обобщённые лямбды (C++14)

Используйте auto в параметрах лямбды:

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

std::cout << add(3, 5) << std::endl;           // 8 (int)
std::cout << add(3.5, 2.7) << std::endl;       // 6.2 (double)
std::cout << add(std::string("Hello"), " World") << std::endl;  // Конкатенация

Лямбды в C++20: concepts и constraints

template<typename T>
concept Addable = requires(T a, T b) { a + b; };

auto safe_add = []<Addable T>(T a, T b) { return a + b; };

Когда использовать лямбды

Используйте лямбды:

  • Для callback функций в алгоритмах STL
  • Для одноразовых операций
  • Когда функция нужна только локально
  • В параллельном коде (std::thread, async)

Избегайте лямбд:

  • Если логика сложная и повторяется
  • Если лямбда становится больше 5-10 строк
  • Для публичных API (используйте явные функции)

Производительность

Лямбды без захватов (или с пустым захватом) компилируются в обычные указатели на функции с нулевых оверхедом. Лямбды с захватом превращаются в функторы и также очень эффективны — компилятор их инлайнит.

Итоговые рекомендации

Лямбда — это мощный инструмент для написания чистого, компактного кода. Она:

  1. Позволяет определить функцию рядом с местом использования
  2. Автоматически захватывает переменные из окружающей области
  3. Идеальна для STL алгоритмов
  4. С C++11+ стала стандартом для коллбэков и функциональных операций
  5. Имеет нулевой оверхед на производительность благодаря инлайнированию

Мастерство в использовании лямбд — признак современного C++ программиста!