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
  • 4,760
  • 2
  • 32
  • 60
  • 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
  • 4,760
  • 2
  • 32
  • 60
Jarod42
  • 190,553
  • 13
  • 166
  • 271
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_a_number
  • 88,680
  • 9
  • 76
  • 150
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);
}
Cássio Renan
  • 5,332
  • 1
  • 24
  • 54
  • How is it deducing `T` in your example? – jaggedSpire Sep 04 '18 at 21:30
  • 1
    Ah, I assumed C++17. I will update with a C++14-enabled example. – Cássio Renan Sep 04 '18 at 21:30
  • 2
    Nice. And conveniently every call is made on the same object instance, so if the lambda has mutable state, it will propagate correctly. – Ben Voigt Sep 04 '18 at 21:53
  • @BenVoigt Thanks. I'm on the impression that keeping that reference to a temporary in the functor is UB, though. I'm gonna edit to correct that. – Cássio Renan Sep 04 '18 at 22:13
  • 1
    Because of lifetime mismatch? IIRC, temporary lifetimes end at the end of the full-expression they're in, so it should be good on that front so long as the `recallable_impl` isn't stored locally. – jaggedSpire Sep 04 '18 at 22:24
  • 3
    @jaggedSpire thanks for the info! Still, from the perspective of a user of this class, accidentally causing UB by simply storing an object sounds like a big no-no. – Cássio Renan Sep 04 '18 at 22:27
  • 2
    Absolutely! [it could be quite problematic.](http://coliru.stacked-crooked.com/a/f9e6754783034824) – jaggedSpire Sep 04 '18 at 22:36
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
  • 4,923
  • 2
  • 22
  • 30
  • 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