8

I wanted to write a lambda that returns itself, so I could call it multiple times on the spot. But it looks like inside of a lambda this refers not to the lambda but to the surrounding object's this, if the lambda is defines inside a member function.

Here's an example:

#include <iostream>

int main(int argc, char* argv[]) {
  int a = 5;
  [&](int b) {
    std::cout << (a + b) << std::endl;
    return *this;
  }(4)(6);
}

Is there a way to do something comparable?

SU3
  • 5,064
  • 3
  • 35
  • 66
  • 1
    Create an old regular functor ? – Jarod42 Sep 04 '18 at 20:45
  • @Jarod42 Thanks. I forgot that we can now define classes within functions. – SU3 Sep 04 '18 at 20:48
  • 5
    Related: [A Proposal to Add Y Combinator to the Standard Library](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0200r0.html). I quote: "C++11/14 lambdas do not encourage recursion: there is no way to reference the lambda object from the body of the lambda function. A common workaround for this problem is to use std::function" – Ben Voigt Sep 04 '18 at 20:58
  • 1
    @HolyBlackCat: A bit of difficulty writing out the type parameter to that `std::function`, there is. – Ben Voigt Sep 04 '18 at 21:06
  • 1
    @SU3: You always could, you just couldn't use local class types as template type parameters before C++11. (Which is not a roadblock to this case) – Ben Voigt Sep 04 '18 at 21:55

4 Answers4

8

With old functor:

int main() {
  int a = 5;
  struct S {
    const S& operator ()(int b) const {
      std::cout << (a + b) << std::endl;
      return *this;
    }
    const int& a;
  };
  S{a}(4)(6);
}

Demo

SU3
  • 5,064
  • 3
  • 35
  • 66
Jarod42
  • 203,559
  • 14
  • 181
  • 302
6

You cant return the lambda itself, but you can return a different one:

#include <iostream>

int main() {
  int a = 5;    
  [&](int b) {
      auto impl = [&](int b){std::cout << (a + b) << std::endl;};
      impl(b);
      return impl;
  }(4)(6);
}

However, this allows only to call it one more time. Not sure if there is some trick to get more out of it...

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
3

Ben Voigt proposes to use Y combinators (which are a great proposal to the standard library, BTW), but your problem is simpler. You can introduce a small template functor that will be called instead of the lambda:

template<typename T>
struct recallable_impl {
    template<typename... Args>
    recallable_impl& operator()(Args&&... args) {
        f(std::forward<Args>(args)...);
        return *this;
    }

    template<typename F>
    recallable_impl(F&& f)
      :  f{std::forward<F>(f)}
    {}
private:
    T f;
};

template<typename T>
decltype(auto) recallable(T&& f) {
    return recallable_impl<std::decay_t<T>>(std::forward<T>(f));
}

Your lambda will then not even need to explicitly return anything:

int main() {
  int a = 5;  
  recallable([&](int b){std::cout << (a + b) << std::endl;})(4)(5)(6)(7)(8);
}
Not a real meerkat
  • 5,604
  • 1
  • 24
  • 55
0

You can call a lambda if you assign it to a predifned type (not auto), and it gets captured:

std::function<void(int)> f =[&f](int n)
{
    if (n>0) f(n-1);
    return;
};
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31
  • 1
    While recursion and returning self both require naming the lambda from inside the lambda, they are still different operations. Recursion is in a sense "easier" because the return type (which has to be named inside of the `std::function` type argument) is simpler. Much simpler. – Ben Voigt Sep 04 '18 at 21:03
  • 1
    Yes.So I would have to declare a type which returns a type, which returns a type, ... OMG it's turtle types all the way down ;-) – Robert Andrzejuk Sep 04 '18 at 21:11