Лямбда-выражения и функторы в C++

Автор: | 13 июня, 2025

В современном C++ лямбда-выражения и функторы (объекты-функции) являются мощными инструментами для создания гибкого и выразительного кода. Они позволяют инкапсулировать поведение и передавать его как объекты, что особенно полезно при работе с алгоритмами STL, асинхронными операциями и многими другими сценариями.

Функторы (объекты-функции) в C++

Что такое функторы?

Функтор — это объект класса, для которого перегружен оператор вызова operator(). Это позволяет использовать объект как функцию.

class Adder {
public:
    Adder(int x) : x_(x) {}
    
    int operator()(int y) const {
        return x_ + y;
    }
    
private:
    int x_;
};

int main() {
    Adder add5(5);
    cout << add5(3); // Выведет 8
}

Преимущества функторов

  1. Состояние: Функторы могут хранить состояние между вызовами
  2. Инкапсуляция: Можно скрыть сложную логику внутри класса
  3. Гибкость: Можно использовать шаблоны и наследование
  4. Производительность: Компилятор может оптимизировать вызовы лучше, чем указатели на функции

Лямбда-выражения в C++

Основы лямбда-выражений

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

Базовый синтаксис:

[захват](параметры) -> возвращаемый_тип { тело_функции }

Пример:

auto lambda = [](int a, int b) { return a + b; };
cout << lambda(2, 3); // 5

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

Лямбда-выражения могут захватывать переменные из окружающего контекста:

По значению:

    int x = 10;
    auto lambda = [x]() { cout << x; };

    По ссылке:

    int y = 20;
    auto lambda = [&y]() { y++; };

    Захват всего контекста:

    [=]() {}; // Все по значению
    [&]() {}; // Все по ссылке

    Возвращаемый тип

    Компилятор может выводить возвращаемый тип автоматически, но его можно указать явно:

    auto lambda = []() -> int { return 42; };

    Изменяемые лямбды

    По умолчанию operator() лямбды const. Для изменения захваченных по значению переменных используйте mutable:

    int counter = 0;
    auto lambda = [counter]() mutable { counter++; };

    Сравнение функторов и лямбда-выражений

    Сходства

    1. Оба могут иметь состояние
    2. Оба могут быть переданы как объекты функций
    3. Оба могут использоваться с шаблонами

    Различия

    1. Синтаксис: Лямбды более компактны
    2. Захват контекста: Лямбды могут легко захватывать локальные переменные
    3. Генерируемый код: Лямбда — это синтаксический сахар для функтора

    Пример эквивалентности:

    // Лямбда
    auto lambda = [x](int y) { return x + y; };
    
    // Эквивалентный функтор
    class LambdaEquivalent {
    public:
        LambdaEquivalent(int x) : x_(x) {}
        int operator()(int y) const { return x_ + y; }
    private:
        int x_;
    };

    Продвинутые техники

    Шаблонные лямбды (C++14)

    В C++14 появились лямбды с auto параметрами:

    auto lambda = [](auto x, auto y) { return x + y; };

    Инициализация в захвате (C++14)

    Можно инициализировать переменные прямо в захвате:

    auto lambda = [value = 42]() { return value; };

    constexpr лямбды (C++17)

    Лямбды могут быть constexpr:

    constexpr auto square = [](int x) { return x * x; };
    static_assert(square(5) == 25);

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

    Для рекурсии нужно использовать std::function:

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

    Практическое применение

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

    vector<int> v = {1, 2, 3, 4, 5};
    // Умножить каждый элемент на 2
    transform(v.begin(), v.end(), v.begin(), [](int x) { return x * 2; });

    Асинхронные операции

    auto future = async([]() {
        this_thread::sleep_for(1s);
        return 42;
    });

    Пользовательские компараторы

    sort(v.begin(), v.end(), [](auto a, auto b) { return a > b; });

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