Магистерский курс C++ (МФТИ, 2022-2023). Лекция 13. Лямбды.

preview_player
Показать описание
Лекции в магистратуре МФТИ по современному C++ на русском языке. Кафедра микропроцессорных технологий.

Центральная тема которую мы здесь обсуждаем это вызываемые объекты. Начиная от чудес std::invoke и далее к лямбда-выражениям, к кортежам и std::apply. Далее мы переходим к стиранию типов. И под конец будет немного развлечения с C++23.

Лектор: Константин Владимиров.
Дата лекции: 30 ноября 2022 года.
Съёмка и звук: Юлий Тарасов.

Timeline:
00:00 Новый синтаксис и лямбды.
06:00 Вызываемые объекты
19:20 Обобщённые лямбды, концепты и явные аргументы
27:10 Основы захвата и финальная прозрачная оболочка
41:50 Вариабельные пачки и каррирование
46:58 Главное правило захвата
54:02 Проброс списка захвата и кортежи
01:09:11 Стирание типов и перегрузка лямбд
01:22:00 Deducing this
01:28:08 Литература и завершение

Errata:
* тут пока пусто
Рекомендации по теме
Комментарии
Автор

Эх, мне бы эту лекцию пару недель назад! Столкнулся на работе с задачей форварда аргументов в лямбду. В итоге дошёл до std::apply с лямбдой и раскрытием пачки, но пришлось конкретно так поковыряться)

Большое спасибо, что выкладываете!

makaedg
Автор

1:08:10 Константин, возможно вы видели, есть пропосал нового statement в язык для итерирования по tuple-like объектам (а также объектам классов) в compile-time: P1306R2 Expansion statements
P.S. Немного пробежавшись глазами по документу, я увидел, что ведутся обсуждения по добавлению в язык возможности итерироваться по пачкам, что довольно круто. Но всё же, через pack indexing можно было бы задать индексы, которые я хочу пробежать

ddvamp
Автор

1:18:50 - в простейшем случае эту технику(пусть даже и как "сахарок" для удобства) можно использовать, например, для описания переходов конечного автомата, где состояния - типы variant'а.

Иногда эту идею развивают и делают FSM более системной: CppCon 2018: Mateusz Pusz “Effective replacement of dynamic polymorphism with std::variant”.

allcreater_
Автор

12:25 Константин, что именно вы подразумеваете под Callable? Стандарт однозначно задаёт function object type [function.objects] и callable type
P.S. Минорно, но к function object type (слайд 5) ещё относятся ссылка на функцию, а также класс с приведением к ссылке на функцию или к ссылке на указатель на функцию

ddvamp
Автор

Относительно примера на 53:00: то есть, a=[]{} и b=[]{} это два разных типа и объекта лямбды, но, если два раза вернуть []{} из функции, мы получим один и тот же объект лямбды?

DanielPinchuk
Автор

Почему msvc не компилятор? В силы его некросплатформенности и closed sourse или у него есть ещё какие-то недостатки?
Кстати спасибо за лекцию, на одном дыхании смотрю ваши лекции вместо аниме и сериалов)

sempaidebil
Автор

Всем привет! На 16:07 в функции print_range параметр e форвардится так : std::forward<delctype(e)>(e). Разве не нужно добавить std::decay std::forward< std::decay_t< delctype(e) > >( e ) ? Ведь е может оказаться ссылочной переменной

anatarev
Автор

0:11:35. Здравствуйте, у foo() в структуре можно const добавить. Ещё можно сделать с & auto r5{ std::invoke(psn, &s) };
0:14:06. А что-то изменится, если мы в лямбде напишем не const auto& p, а auto&& p?

dmitrydemis
Автор

Касательно правила на 47:50. А что на счёт константного локального нестатического контекста? MSVC, например, не обязывает захватывать в лямбду локальные константные и constexpr переменные.

danielkeehl
Автор

1:06:25 Константин, "This parrot is no more!". forward_as_tuple пораждает набор (!) ссылок, что противоречит исходной задаче: передать xvalue по значению. Поэтому, при передаче в foo prvalue, лямбда переживёт их. Пример:
#include <iostream>
#include <tuple>

struct Parrot {
~Parrot() { std::cout << "Parrot is dead"; }
};

int main() {
auto foo = []<typename ...T>(T &&...t) noexcept {
return [a = mutable noexcept {
(void) std::get<0>(a);
};
};

[[maybe_unused]] auto l = foo(Parrot{});

std::cout << " before use";

return 0;
}

Правильный способ состоит в ручном конструировании tuple: [a = std::tuple<T...>(std::forward<T>(t)...)], причём нельзя полагаться на CTAD (выведет всё как значения)

Стоит отметить, что допустимо использовать T и в теле конструируемой лямбды: [&a = t] { std::forward<T>(a); }, поэтому, если мы уверены, что все инициализаторы переживут объект замыкания, а особенно с С++26 (Pack Indexing: P2662R3), можно делать так:
auto foo = []<typename ...T>(T &&...t) noexcept {
return [&] noexcept {
(void)
}
}
что, правда, немного многословнее нежели std::get<0>(t) для tuple

P.S. Как жаль, что в pack indexing не завезли выражения: std::forward<T>(t)...[0]

ddvamp
Автор

спасибо большое за лекцию, как всегда узнал много нового!

хотел подметить, что вы становитесь хороши в кликбейте — превьюшка, где вы хватаетесь за голову, сильно подогревает интерес к видео :)

sigasigasiga
Автор

Интересно было бы послушать про суть техники стирания типов, как она под капотом работает. Может быть есть/планируется лекция по этому вопросу?

danielkeehl
Автор

Подскажите, пожалуйста, по 60 слайду: self же выведения в лямбду, которая принимает Node*, а мы хотим, чтобы в visit передавался весь overload

VisibleToAllYTUsers
Автор

Разве на слайде 37 не нужно писать Или это было ощущено для красоты слайда?

АндрейШерстобитов-вд
Автор

Кстати, на 25 слайде порядок вызова вычислений не гарантирован, и не факт что будет 21, 52, 74, 106. Прямое копирование слейда у меня дало 106, 85, 54, 32. Это на mingw GCC 6.3 с -std=c++17 .

DrUlrih
Автор

30:05 Объясните, все же, почему этот способ омерзителен? Чем способ с переименованием [&r = std::as_const(a)] лучше?

ddcd
Автор

Можно ли получить доступ к домашним заданиям?

illiapechonkin
Автор

30:08 а как? 🤔
clang 14 конструкции вида [const &x] и [& const x] не компилирует

sigasigasiga