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

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

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

[ capture list ] ( argument list ) -> return type { function body }
  • capture list используется для указания переменных из окружающей области, которые будут доступны в лямбда-выражении. Переменные можно захватывать по значению, по ссылке или с помощью this.
  • argument list указывает параметры, которые будут переданы в лямбду.
  • return type указывает тип значения, которое лямбда вернет. Если он не указан, компилятор попытается его вывести.
  • function body указывает код, который будет выполняться при вызове лямбды.

Вот несколько различных способов использования лямбда-выражений в C++:

  1. Обратные вызовы функций
  2. Захват по умолчанию
  3. Захват по значению
  4. Захват по ссылке
  5. Изменяемые лямбда-выражения

{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++.

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

  1. Встроенные функции. Лямбда-выражения автоматически встраиваются компилятором, что означает, что их код напрямую вставляется в вызывающую функцию. Это может уменьшить накладные расходы на вызовы функций и повысить производительность.
  2. Избегайте накладных расходов на именованные функции. У лямбда-выражений нет имен, поэтому их не нужно объявлять и хранить в таблице символов, что может снизить накладные расходы и повысить производительность.
  3. Улучшенная локализация кэша: лямбда-выражения можно определять и использовать в одной и той же функции, что означает, что код и данные, используемые лямбда-выражением, будут храниться в той же строке кэша, что и вызывающий код. Это может улучшить локальность кеша и снизить стоимость промахов кеша.
  4. Уменьшенный размер кода. Лямбда-выражения обычно меньше именованных функций и не требуют внешних вызовов функций, что может уменьшить размер скомпилированного кода и повысить производительность.
  5. Повышенная гибкость. Лямбда-выражения можно использовать для передачи функций в качестве аргументов другим функциям, что обеспечивает большую гибкость при повторном использовании и организации кода. Это может повысить производительность за счет уменьшения необходимости дублирования кода.
  6. Улучшенная читаемость: Lambdas может сделать код более читаемым, инкапсулируя сложную логику в компактной и автономной форме. Это может повысить производительность за счет упрощения понимания и поддержки кода.

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

Лучшие