Различные статьи по Qt и c++

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

обновлено 127 просмотров

В современном 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, значительно упростили написание анонимных функций, в то время как функторы предоставляют больше контроля и возможностей для сложных сценариев.