Лямбда-выражения — это анонимные функции (функции без имени), которые можно определить и использовать в C++. Они обеспечивают удобный способ передачи небольших фрагментов кода в качестве аргументов другим функциям или определения функции на месте без необходимости создания именованной функции.
Лямбда-выражения обеспечивают гибкий и лаконичный способ написания функциональных объектов на C++ и широко используются в современном программировании на C++.
Лямбды определяются с использованием следующего синтаксиса:
[ capture list ] ( argument list ) -> return type { function body }
capture list
используется для указания переменных из окружающей области, которые будут доступны в лямбда-выражении. Переменные можно захватывать по значению, по ссылке или с помощьюthis
.argument list
указывает параметры, которые будут переданы в лямбду.return type
указывает тип значения, которое лямбда вернет. Если он не указан, компилятор попытается его вывести.function body
указывает код, который будет выполняться при вызове лямбды.
Вот несколько различных способов использования лямбда-выражений в C++:
- Обратные вызовы функций
- Захват по умолчанию
- Захват по значению
- Захват по ссылке
- Изменяемые лямбда-выражения
{1} Обратные вызовы функций
Обратный вызов функции — это функция, которая передается в качестве аргумента другой функции и вызывается принимающей функцией позднее. Вы можете передать лямбду в качестве аргумента функции, где она будет выполняться при наступлении определенного события.
Пример:
#include <iostream> #include <algorithm> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; // Lambda expression to find the sum of two numbers auto sum = [](int a, int b) { return a + b; }; int result = std::accumulate(numbers.begin(), numbers.end(), 0, sum); std::cout << "Sum of elements in the vector: " << result << std::endl; return 0; }
В этом примере переменная sum
представляет собой лямбда-выражение, которое принимает два параметра a
и b
и возвращает их сумму. Функция std::accumulate
принимает вектор чисел, начальное значение результата и функцию суммы (лямбда-выражение). Функция вычисляет сумму всех элементов вектора и возвращает результат, который выводится на экран.
Другой пример:
#include <iostream> #include <algorithm> #include <vector> int main() { std::vector<int> vec = { 1, 2, 3, 4, 5 }; int sum = 0; std::for_each(vec.begin(), vec.end(), [&sum](int x) { sum += x; }); std::cout << "The sum is: " << sum << std::endl; return 0; }
В этом случае лямбда-выражение [&sum](int x) { sum += x; }
передается как функция, применяемая к каждому элементу. Лямбда-выражение захватывает переменную sum
по ссылке &
, чтобы ее можно было изменить в теле лямбда-выражения.
Оба примера дают один и тот же результат, но во втором примере используется алгоритм std::for_each
и лямбда-выражение, которые являются более современными и лаконичными методами в C++.
{2} Запись по умолчанию
Когда лямбда-выражение объявляется без каких-либо явных захватов, поведение по умолчанию — захват переменных в окружающей области по ссылке. Это называется захватом значений по умолчанию.
Примеры:
#include <iostream> int main() { int x = 42; auto f = [ ]() { std::cout << x << std::endl; }; f(); return 0; } #include <iostream> int main() { auto square = [](int x) { return x * x; }; std::cout << "The square of 5 is: " << square(5) << std::endl; return 0; }
Во втором примере лямбда-выражение определяется и сохраняется в переменной с именем square
. Это лямбда-выражение принимает аргумент int
x
и возвращает значение x * x
, которое является квадратом аргумента.
В функции main
это лямбда-выражение используется как объект функции. Он вызывается путем передачи аргумента 5
, а результат отображается с использованием потока cout
.
{3} Захват по значению
Это простейшая форма лямбда-выражения, в которой вы передаете переменные в функцию по значению. Когда переменная захватывается по значению, ее текущее значение сохраняется в замыкании и не обновляется при изменении переменной в окружающей области. Это делается путем включения переменной в квадратные скобки [ ]
Примеры
#include <iostream> int main() { int x = 42; auto f = [x]() { std::cout << x << std::endl; }; f(); return 0; } #include <iostream> int main() { int x = 42; auto f = [x](int y) { std::cout << x+y << std::endl;}; f(1); return 0; }
{4} Захват по ссылке
Вы можете передавать переменные в лямбда-выражение по ссылке, используя символ &
. Когда переменная захватывается по ссылке, ее текущее значение сохраняется в замыкании и обновляется при изменении переменной в окружающей области. Это делается путем включения оператора адреса &
перед переменной в квадратных скобках [ ]
.
Примеры
#include <iostream> int main() { int x = 42; auto f = [&x]() { std::cout << x << std::endl; }; f(); return 0; } #include <iostream> int main() { int x = 10; auto add_one = [&x]() { ++x; }; add_one(); std::cout << x << "\n"; return 0; } #include <iostream> int main() { int x = 42; auto f = [&x]() { std::cout << x << std::endl; }; f(); return 0; }
В последнем примере переменная x
захватывается по ссылке, и лямбда add
может изменять ее значение.
{5} Изменяемые лямбда-выражения
По умолчанию переменные, захватываемые лямбда-выражением, являются постоянными и не могут быть изменены в теле лямбда-выражения. Если вы хотите изменить захваченные переменные в лямбда-выражении, вы можете сделать лямбда-выражение изменяемым. Изменяемые лямбда-выражения позволяют изменять захваченные переменные. Это делается путем включения ключевого слова mutable
в квадратные скобки [ ]
.
Пример
#include <iostream> int main() { int x = 42; auto f = [x]() mutable { std::cout << ++x << std::endl; }; f(); return 0; }
{Заключение}
Лямбда-выражение похоже на обычную функцию, но имеет некоторые ключевые отличия. Например, тип лямбда-выражения не указывается явно, но компилятор может его определить. Кроме того, лямбда-выражения могут захватывать переменные из окружающей области, что делает их очень полезными для создания замыканий и работы с концепциями функционального программирования на C++.
Лямбда-выражения имеют некоторые преимущества в производительности по сравнению с традиционными функциями.
- Встроенные функции. Лямбда-выражения автоматически встраиваются компилятором, что означает, что их код напрямую вставляется в вызывающую функцию. Это может уменьшить накладные расходы на вызовы функций и повысить производительность.
- Избегайте накладных расходов на именованные функции. У лямбда-выражений нет имен, поэтому их не нужно объявлять и хранить в таблице символов, что может снизить накладные расходы и повысить производительность.
- Улучшенная локализация кэша: лямбда-выражения можно определять и использовать в одной и той же функции, что означает, что код и данные, используемые лямбда-выражением, будут храниться в той же строке кэша, что и вызывающий код. Это может улучшить локальность кеша и снизить стоимость промахов кеша.
- Уменьшенный размер кода. Лямбда-выражения обычно меньше именованных функций и не требуют внешних вызовов функций, что может уменьшить размер скомпилированного кода и повысить производительность.
- Повышенная гибкость. Лямбда-выражения можно использовать для передачи функций в качестве аргументов другим функциям, что обеспечивает большую гибкость при повторном использовании и организации кода. Это может повысить производительность за счет уменьшения необходимости дублирования кода.
- Улучшенная читаемость: Lambdas может сделать код более читаемым, инкапсулируя сложную логику в компактной и автономной форме. Это может повысить производительность за счет упрощения понимания и поддержки кода.
Таким образом, лямбда-выражения могут обеспечить более высокую производительность по сравнению с традиционными функциями за счет сокращения накладных расходов, улучшения локальности кэша, уменьшения размера кода, повышения гибкости и улучшения читабельности.
Лучшие