Лямбды
Общие сведения о лямбда-функциях
Лямбда-функции определяются с помощью синтаксиса []() {}
. Они могут принимать параметры, возвращать значения и захватывать переменные из окружающего контекста.
#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++ является объектом, который включает в себя:
- Тело функции.
- Захваченные переменные (если есть).
Когда лямбда-функция объявляется, компилятор генерирует анонимный класс, который содержит:
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
. Это обеспечивает гибкость и безопасность, но может добавить некоторые накладные расходы на производительность и память.