Перейти к содержимому

Лямбды

Общие сведения о лямбда-функциях

Лямбда-функции определяются с помощью синтаксиса []() {}. Они могут принимать параметры, возвращать значения и захватывать переменные из окружающего контекста.

#include <iostream>
int main() {
auto add = [](int a, int b) {
return a + b;
};
std::cout << "Sum: " << add(3, 4) << std::endl; // Вывод: Sum: 7
return 0;
}

В этом примере лямбда-функция add складывает два числа.

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

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

  • По значению [=]
  • По ссылке [&]
  • Конкретные переменные по значению [a, b]
  • Конкретные переменные по ссылке [&a, &b]
#include <iostream>
int main() {
int x = 10;
int y = 20;
auto add = [x, y]() {
return x + y;
};
std::cout << "Sum: " << add() << std::endl; // Вывод: Sum: 30
return 0;
}

Здесь лямбда-функция захватывает переменные x и y по значению.

Использование лямбда-функций

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

#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
std::sort(vec.begin(), vec.end(), [](int a, int b) {
return a < b;
});
for (int n : vec) {
std::cout << n << " ";
}
// Вывод: 1 1 2 3 3 4 5 5 5 6 9
return 0;
}

В данном примере лямбда-функция используется для сортировки вектора.

Лямбда-функции и шаблоны

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

#include <iostream>
template <typename Func>
void apply(Func f, int a, int b) {
std::cout << "Result: " << f(a, b) << std::endl;
}
int main() {
apply([](int a, int b) { return a * b; }, 4, 5); // Вывод: Result: 20
return 0;
}

Здесь шаблонная функция apply принимает лямбда-функцию в качестве аргумента.

Хранение лямбда-функций

Внутреннее устройство лямбда-функций

Каждая лямбда-функция в C++ является объектом, который включает в себя:

  1. Тело функции.
  2. Захваченные переменные (если есть).

Когда лямбда-функция объявляется, компилятор генерирует анонимный класс, который содержит:

  • operator(), представляющий тело лямбда-функции.
  • Члены данных для хранения захваченных переменных.

Рассмотрим пример:

#include <iostream>
int main() {
int a = 5;
int b = 10;
auto lambda = [a, &b](int x) {
return a + b + x;
};
b = 15;
std::cout << lambda(20) << std::endl; // Вывод: 40 (5 + 15 + 20)
return 0;
}

В этом примере компилятор создает анонимный класс с членами a и &b, а также методом operator()(int x).

std::function и лямбда-функции

Для хранения лямбда-функций, особенно тех, которые захватывают переменные, часто используется std::function. Это шаблонный класс, который может хранить любые вызываемые объекты (включая функции, лямбда-функции, функторы и т. д.).

#include <functional>
#include <iostream>
int main() {
int x = 10;
std::function<int(int)> add_x = [x](int y) {
return x + y;
};
std::cout << add_x(5) << std::endl; // Вывод: 15
return 0;
}

В данном примере std::function<int(int)> используется для хранения лямбда-функции, которая захватывает переменную x.

Преимущества и недостатки использования std::function

Преимущества

  • Гибкость: std::function может хранить любые вызываемые объекты, включая лямбда-функции с захватом.
  • Безопасность типов: std::function обеспечивает строгую проверку типов, что уменьшает вероятность ошибок.

Недостатки

  • Накладные расходы: Использование std::function может добавить некоторые накладные расходы на производительность и память из-за динамического выделения памяти и механизма виртуальных функций.
  • Необходимость шаблонного параметра: Требуется явное указание сигнатуры функции, что может быть не всегда удобно.

Пример хранения лямбда-функции без std::function

Иногда лямбда-функцию можно сохранить напрямую в переменную автоматического типа, если она не захватывает переменные:

#include <iostream>
int main() {
auto multiply = [](int a, int b) {
return a * b;
};
std::cout << multiply(3, 4) << std::endl; // Вывод: 12
return 0;
}

Резюме

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