Pull to refresh

Comments 15

Забавная, конечно, дичь. Практическое применение я смог придумать только примеру №3. Можно забабахать такую-себе ручную таблицу виртуальных функций вне контекста класса. Не знаю зачем, но можно.

Ну, хранение лямбд в контейнере может быть полезно при реализации механизма сигналов-слотов. При генерации сигнала, все функции в контейнере (слоты) вызываются по очереди.

Автор оригинала забыл опустить наверное самую важную особенность лямбд, введённую с C++14 -- это generic lambda. В сочетании с fold expression (c++ 17) можно теперь не городить лютые шаблоны, а делать всё куда лакончинее и проще:

#include <iostream>

#include <string>

int main()

{

    auto sum = [](auto x, auto y, auto... args)

    {

        return ((x + y) + ... + args);

    };

    //prints: 8

    std::cout << sum(3, 5) << std::endl; 

    //prints: hello world one two 

    std::cout << sum(std::string{"hello "}, "world ", "one ", "two") << std::endl;

    return 0;

}

Фактически, лямбды стали синтаксическим сахаром над шаблонными функторами.

Мы на курсе делали интерпретатор упрощенного аналога Python. Там как раз функциональные объекты (выражения) сначала добавлялись в контейнер, а потом последовательно из него вызывались. Та была еще задачка.

Как обычно, переводчик изнасиловал статью:

Общие лямбды и их вывод

Возвращение лямбды

Вот пример аналога рекурсивной лямбда-функции без std::function:

struct {
  void operator()() {
    return (*this)();
  }
} recusive_lambda;
recursive_lambda();

Да, это анонимная структура, но после компиляции останется лишь вызов перегрузки оператора, результат будет идентичен объявлению и вызову обычной лямбда-функции, но теперь у нас есть указатель this... но вот только теперь мы жертвуем лямбда-захватом, что в принципе сложно эмитировать при некоторых обстаятельствах. Кстати этот вариант лямбды можно засунуть в std::function , а следовательно и передовать в аргументы других функций, главное чтобы сигнатура перегрузки оператора совпадала с сигнатурой объявленной в std::function

Захват можно делать руками через конструктор. Но с лайфтаймом придется повозиться.

А можно поподробнее по поводу: Хранение лямбд в контейнере + Возможно, это не совсем правильно... + можно сделать небольшой хак и хранить все лямбды как объекты std::function
?
Я себя данный подход использую во всю. Почему это не совсем правильно? почему хак? Я не прав так делая? Какие подводне камни здесь есть?

Рекурсивные лямбды - это, внезапно, именно то, как делаются рекурсивные функции в лямбда-исчислении. С помощью Y-комбинатора.

Как по-быстрому и по-красивому прикрутить сюда карринг, у меня сейчас фантазии не хватило, поэтому фиг с ним, пусть будет бойлерплейт с вызовом g(g,....).

auto Y = [](auto f) { return [f](auto x) { return f(f, x); }; };

int main() {
  auto fac = Y([](auto g, unsigned k) -> unsigned { return k==0 ? 1 : k*g(g, k-1); });

  printf("%u", fac(5));
}

Шел 2021 год. Я зык C++ медленно но верно деградировал в perl, где вместо того чтобы разбираться что хотел сказать тем или иным кодом очередной шизофреник было легче его переписать.

auto CreateLambda(int y) {
    return [&y](int x) { return x + y; };
}

Тут мы точно хотим захватывать y по ссылке?

Есть предложение в стандарт, которое вероятно попадет в ближайшие версии C++, в нем вводятся рекурсивные лямбды - self это неявный аргумент, являет собой объект closure.

auto fib = [](this auto self, int n) {
    if (n < 2) return n;
    return self(n-1) + self(n-2);
};
А почему сразу this не сделать доступным внутри лямбды? Все равно ведь это класс с оператором вызова. Использовал this в теле лямбды — сказал компилятору: «не оптимизируй/не инлайнь».

Исчезнет возможность подхватывать this класса где создаётся лямбда через лямбда-захват:

class SomeClass {
	int a = 255;
public:
  auto getLambda();
};

auto SomeClass::getLambda() {
	return [this](int a) {return a * this->a;}
}
Sign up to leave a comment.