Pull to refresh
954.64
OTUS
Цифровые навыки от ведущих экспертов

Краткий обзор развития лямбда-выражений в C++11, C++14, C++17 и C++20

Level of difficultyEasy
Reading time4 min
Views12K

Привет, Хабр!

Сегодня рассмотрим лямбда-выражения в C++ и их эволюцию с момента появления в стандарте C++11 и до последних обновлений в C++20.

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

Лямбда-выражения в C++11

Лямбда-выражение в C++11 можно объявить следующим образом:

auto myLambda = []() { /* тело лямбды */ };

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

Захват переменных позволяет лямбда-выражениям использовать данные из окружающего контекста. Доступны следующие способы захвата:

  • [=] захватывает все переменные из области видимости по значению.

  • [&] захватывает все переменные по ссылке.

  • [this] захватывает указатель this для доступа к членам класса.

  • [var] или [&var] захватывает конкретную переменную по значению или по ссылке соответственно.

Простой пример:

std::vector<int> nums = {1, 2, 3, 4, 5};
std::for_each(nums.begin(), nums.end(), [](int n) { std::cout << n << " "; });

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

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

int multiplier = 5;
auto multiply = [multiplier](int n) { return n * multiplier; };
std::cout << multiply(10); // Выведет 50

Лямбда захватывает переменную multiplier по значению и использует ее для умножения входного параметра.

Лямбда в C++14

Одна из лучших фич в C++14 была связана с автоматическим выводом типов возвращаемого значения лямбда-выражений. Т.е наконец не обязательно явно указывать тип возвращаемого значения — компилятор может самостоятельно его определить. И кстати, C++14 ввел возможность инициализации захваченных переменных прямо в лямбда-интродукторе

Итак, в C++11 тип возвращаемого значения лямбда-выражения нужно было указывать явно, если оно не было очевидным. В C++14 этого уже не требуется благодаря автоматическому выводу типов:

Пример без C++14:

auto lambda = []() -> int { return 42; };

Пример с C++14:

auto lambda = []() { return 42; }; //  тип значения int выводится автоматически

C++14 позволяет инициализировать переменные непосредственно в лямбда-интродукторе:

auto lambda = [x = 42]() { return x; };

x инициализируется значением 42 и захватывается лямбда-выражением.

Пример с автоматическим выводом типов:

std::vector<int> nums = {1, 2, 3, 4, 5};
auto evenCount = std::count_if(nums.begin(), nums.end(), [](int n) { return n % 2 == 0; });
std::cout << "Количество четных чисел: " << evenCount << std::endl;

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

Пример с инициализацией захваченных переменных:

auto adder = [value = 10](int x) { return x + value; };
std::cout << adder(5); // 15

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

Нововведения в C++17

Здесь появилась крутая фича с захватом*this в лямбда-выражениях. Можно захватывать текущий объект по значению, мастхев при работе с классами или объектами

Рассмотрим класс, в котором нужно использовать лямбда-выражение, захватывающее все локальные переменные по значению и текущий объект класса тоже по значению:

class MyClass {
public:
    int value = 10;

    void printValue() {
        auto lambda = [=, *this]() {
            std::cout << "Value: " << value << std::endl;
        };
        lambda();
    }
};

int main() {
    MyClass obj;
    obj.printValue(); // Выведет: Value: 10
}

Лямбда захватывает текущий объект *this по значению и так можно использовать члены класса внутри лямбды.

В C++17 лямбды теперь могут быть выполнены на этапе компиляции(constexpr):

constexpr auto square = [](int n) constexpr {
    return n * n;
};

int main() {
    constexpr int result = square(5);
    static_assert(result == 25, "The result must be 25");
}

square является constexpr лямбда-выражением, а так его можно вычислить на этапе компиляции.

Также C++17 наконец стали довольно часто юзать stl. Вообще, можно было юзать и начиная с версии 14, но только в 17 версии это обрело свою массовость:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // auto в параметрах лямбда-выражения
    auto isEven = [](auto n) { return n % 2 == 0; };

    auto it = std::find_if(numbers.begin(), numbers.end(), isEven);

    if (it != numbers.end()) {
        std::cout << "Первое четное число: " << *it << std::endl;
    }
}

Параметр auto позволяет его использовать с любым типом, поддерживающим операцию %.

C++20

C версии C++20 лямбду можно использовать вместе с шаблонами:

auto genericLambda = []<typename T>(T x) {
    std::cout << x << std::endl;
};

genericLambda(10);    // воркает с int
genericLambda("Test"); // с const char*

Концепты в C++20 позволили реализовать строгую типизацию и сделать более понятные требования к шаблонам:

#include <concepts>
#include <iostream>

template<typename T>
concept Addable = requires(T a, T b) {
    { a + b } -> std::convertible_to<T>;
};

auto add = []<Addable T>(T a, T b) {
    return a + b;
};

int main() {
    std::cout << add(1, 2) << std::endl; //  3
    // std::cout << add("a", "b") << std::endl; // ошибка компиляции, так как "a" и "b" не удовлетворяют концепту Addable
}

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

Статья подготовлена в рамках запуска специализации C++. Хочу поделиться с вами ссылками на бесплатные вебинары специализации:

Tags:
Hubs:
Total votes 27: ↑20 and ↓7+13
Comments201

Articles

Information

Website
otus.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
OTUS