<MyRusakov.ru />

Профессия Web-разработчик

Профессия Web-разработчик

Этот комплект за 8 месяцев превратит Вас в Web-разработчика с нуля. Учиться достаточно 1 час в день.

Начнёте Вы с HTML, CSS и вёрстки сайтов. Потом перейдёте к программированию и JavaScript. Затем изучите PHP, MySQL, SQL, Python. Изучите Web-фреймворки Laravel и Django. Создадите 5 своих сайтов для портфолио.

Комплект содержит:

- 540 видеоуроков

- 110 часов видео

- 1205 заданий для закрепления материала из уроков

- 5 финальных тестов

- 7 сертификатов

- 12 Бонусных курсов

Подробнее
Подписка

Подпишитесь на мой канал на YouTube, где я регулярно публикую новые видео.

YouTube Подписаться

Подписавшись по E-mail, Вы будете получать уведомления о новых статьях.

Подписка Подписаться

Добавляйтесь ко мне в друзья ВКонтакте! Отзывы о сайте и обо мне оставляйте в моей группе.

Мой аккаунт Мой аккаунт Моя группа
Опрос

Какая тема Вас интересует больше?

Лямбда‑функции в C++: захваты, mutable, возвращаемые типы и лучшие практики

Лямбда‑функции в C++: захваты, mutable, возвращаемые типы и лучшие практики

Запрос, под который оптимизирована статья: лямбда‑функции в C++. Если вы хотите быстро и безопасно писать компактные функции прямо в месте вызова — лямбды незаменимы. Разберёмся на практических примерах, без лишней теории.

Базовый синтаксис лямбда‑функций

Общий вид: [](параметры) спецификаторы -> возвращаемый_тип { тело; }. Минимальная лямбда:

#include <iostream>

int main() {
    auto hello = [] { std::cout << "Hello, lambda!\n"; };
    hello();
}

Захваты: по значению, по ссылке и смешанные

Захват управляет тем, какие внешние переменные доступны внутри лямбды.

  • [] — ничего не захватываем
  • [=] — по значению (копии) всех используемых переменных
  • [&] — по ссылке всех используемых переменных
  • [x, &y] — явный смешанный захват
  • [this] — захват указателя this (копий нет)
  • [*this] (C++23) — копия объекта, на который указывает this
#include <algorithm>
#include <iostream>
#include <vector>

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

    // Сортируем так, чтобы числа < pivot были первыми
    std::sort(v.begin(), v.end(), [=](int a, int b) {
        auto ka = (a < pivot);
        auto kb = (b < pivot);
        if (ka != kb) return ka > kb; // < перед > — логика группировки
        return a < b;
    });

    for (int x : v) std::cout << x << ' ';
}

Совет: по умолчанию используйте явный захват (например, [pivot]). Глобальные [=] и [&] удобны, но повышают риск случайно захватить лишнее и усложнить сопровождение.

Инициализирующий и перемещающий захват (C++14+)

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

#include <memory>
#include <thread>
#include <iostream>

int main() {
    auto p = std::make_unique<int>(42);
    std::thread t([q = std::move(p)]() {
        // q - единственный владелец внутри лямбды
        std::cout << *q << "\n";
    });
    t.join();
    // p теперь пуст (nullptr)
}

Такой приём безопасен в многопоточных сценариях: вы явно передаёте владение в лямбду.

mutable: изменение копий, а не внешних значений

Лямбды по умолчанию «константные»: захваченные по значению переменные менять нельзя. mutable снимает это ограничение, но меняет только копии.

#include <iostream>

int main() {
    int n = 0;
    auto inc_copy = [n]() mutable {
        n++;              // ок, меняем копию
        return n;
    };
    std::cout << inc_copy() << ", " << inc_copy() << "\n"; // 1, 2
    std::cout << n << "\n"; // исходный n по-прежнему 0
}

Чтобы менять внешнюю переменную, захватывайте по ссылке: [&n]{ n++; }.

Возвращаемые типы: auto и явное указание

Компилятор выводит тип результата автоматически, но если в ветках возвращаются разные типы, нужна явная аннотация.

auto f = [](bool flag) {
    if (flag) return 1;      // int
    // return 1.5;           // ошибка: разные типы
    return 2;                // ок, везде int
};

auto g = [](bool flag) -> double {
    if (flag) return 1;      // 1.0
    return 1.5;              // 1.5
};

Generic‑лямбды (C++14+): параметры как auto

Идеально подходят для алгоритмов стандартной библиотеки и универсальных коллбеков без явного шаблонного синтаксиса.

#include <algorithm>
#include <cctype>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> words{"C++", "Lambda", "Rocks"};
    std::transform(words.begin(), words.end(), words.begin(), [](auto s) {
        for (auto& ch : s) ch = std::toupper(static_cast(ch));
        return s;
    });
}

Лямбды в алгоритмах: фильтрация и подсчёт

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

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

    v.erase(std::remove_if(v.begin(), v.end(), [](int x){ return x % 2 == 0; }), v.end());
    // Остались только нечётные: 1 3 5
    for (int x : v) std::cout << x << ' ';
}

std::function и лямбды: когда это оправдано

std::function — тип-обёртка для хранение вызываемого объекта с типовым стиранием. Это гибко, но медленнее и требует выделений. Рекомендации:

  • Внутри шаблонов и локально — храните лямбду в auto или передавайте как универсальный параметр (perfect forwarding).
  • На границах API (коллбеки, полиморфные контейнеры) — используйте std::function<...> для стабильного интерфейса.
#include <functional>
#include <iostream>

void run(std::function<void(int)> cb) { cb(42); }

int main() {
    auto fast = [](int x){ std::cout << x << "\n"; };
    run(fast); // удобно на границе API
}

Статeless‑лямбда как указатель на функцию

Если лямбда ничего не захватывает, её можно неявно преобразовать к указателю на функцию подходящей сигнатуры — удобно для старых C‑API.

void (*fp)(int) = [](int x){ /* ... */ };
fp(10);

Типичные ошибки и как их избежать

  • Дэнглинг‑ссылки: вы захватили ссылкой переменную, которая вышла из области видимости. Правило — для асинхронных задач используйте захват по значению или перемещающий захват.
  • Глобальные [=] или [&]: скрытые зависимости. Предпочитайте явный захват.
  • Тяжёлые копии: большие объекты лучше захватывать по ссылке или по перемещению.
  • Сложные типы возвращаемых значений: при неоднородных ветках укажите -> тип явно.

Небольшой чек‑лист по лямбдам

  • Пишите очевидный список захвата: [x], [&y], [z = make()].
  • Используйте mutable, когда меняете копию захваченного значения внутри лямбды.
  • Предпочитайте auto для хранения лямбды, а std::function — для публичных API.
  • Для многопоточности — перемещающий захват ресурсов ([p = std::move(p)]).
  • Статeless‑лямбды легко передавать туда, где ожидают указатель на функцию.

Куда двигаться дальше

Потренируйтесь переписать свои sort/filter/remove_if на лямбды, попробуйте перемещающий захват и generic‑лямбды. А чтобы системно закрыть пробелы по современному C++ (включая лямбды, ООП, STL и практику), посмотрите программу и первые уроки здесь: C++ с Нуля до Гуру — посмотреть программу и начать бесплатно.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (https://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: https://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: https://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как создать свой сайт

  2. Текстовая ссылка:

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (0):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.