Lambda nad Lambda in C ++ 14

How is the completion / completion of a recursive lambda call?

#include <cstdio> auto terminal = [](auto term) // <---------+ { // | return [=] (auto func) // | ??? { // | return terminal(func(term)); // >---------+ }; }; auto main() -> int { auto hello =[](auto s){ fprintf(s,"Hello\n"); return s; }; auto world =[](auto s){ fprintf(s,"World\n"); return s; }; terminal(stdout) (hello) (world) ; return 0; } 

What am I missing here?

Running code

+70
c ++ lambda c ++ 14
02 Sep '14 at 8:23
source share
6 answers

This is not a recursive function call, a step-by-step look at it:

  • terminal(stdout) - this just returns the lambda that captured stdout
  • Result 1. is called with lambda hello , which executes lambda ( func(term) ), the result of which is passed to terminal() , which simply returns lambda, as in 1.
  • Result 2. is called with lambda world , which does the same as 2, this time the return value is discarded ...
+46
Sep 02 '14 at 8:37
source share

The call itself is not recursive. It returns a function object, which, if called, will call terminal again to create another function object.

So, terminal(stdout) returns a functor that captures stdout and can be called by another function object. Calling it again, (hello) , calls the hello functor with the captured member stdout , outputting "Hello" ; calls terminal and returns another functor, which this time captures the return value of hello - which is still stdout . Call this functor (world) , over and over again, displaying "World" .

+26
Sep 02 '14 at 8:33
source share

The key here is to understand that this is true:

 world(hello(stdout)); 

and prints "Hello World." The recursive lambda series can be deployed as

 #include <cstdio> auto terminal = [](auto term) // <---------+ { // | return [=] (auto func) // | ??? { // | return terminal(func(term)); // >---------+ }; }; /* terminal(stdout) -returns> anonymous_lambda which captures stdout (functor) anonymous_lambda(hello) is called, func(term) is hello(stdout) and prints "Hello" and returns stdout, the anonymous_lambda -returns> terminal(stdout) (the above 2 lines start again) terminal(stdout) is called and -returns> anonymous_lambda which captures stdout (functor) anonymous_lambda(world) is called, func(term) is world(stdout) and prints "World" and returns stdout, the anonymous_lambda -returns> terminal(stdout) terminal(stdout) is called and -returns> anonymous_lambda which captures stdout (functor) nobody uses that anonymous_lambda.. end. */ auto main() -> int { auto hello =[](auto s){ fprintf(s,"Hello\n"); return s; }; auto world =[](auto s){ fprintf(s,"World\n"); return s; }; world(hello(stdout)); terminal(stdout) (hello) (world) ; return 0; } 

Coliru example

+13
02 Sep '14 at 8:58
source share

It can be internally translated into something that looks like this:

 #include <cstdio> template <typename T> struct unnamed_lambda { unnamed_lambda(T term) : captured_term(term) {} template <typename A> unnamed_lambda operator()(A func); T captured_term; }; struct terminal_lambda { template <typename A> unnamed_lambda<A> operator()(A term) { return unnamed_lambda<A>{term}; } }; terminal_lambda terminal; template <typename T> template <typename A> unnamed_lambda<T> unnamed_lambda<T>::operator()(A func) { return terminal(func(captured_term)); } struct Hello { FILE* operator()(FILE* s) { fprintf(s, "Hello\n"); return s; } }; struct World { FILE* operator()(FILE* s) { fprintf(s, "World\n"); return s; } }; int main() { Hello hello; World world; unnamed_lambda<FILE*> l1 = terminal(stdout); unnamed_lambda<FILE*> l2 = l1(hello); unnamed_lambda<FILE*> l3 = l2(world); // same as: terminal(stdout)(hello)(world); } 

Live demo

This is actually what the compiler does behind the scenes with lambdas (with some approximation).

+10
Sep 02 '14 at 8:53
source share

I think the source of the confusion is to read the lambda declaration as a lambda call. Valid here:

 auto terminal = [](auto term) // <---------+ { // | return [=] (auto func) // | ??? { // | return terminal(func(term)); // >---------+ }; }; 

the author simply declared a lambda terminal , which takes one arbitrary argument to term and returns an unnamed lambda, nothing more! Let's look at this unnamed lambda, it:

  • takes the called func object as an argument and calls it in the term parameter and
  • returns the result of calling the terminal with the result of calling func(term) ; therefore, it returns another unnamed lambda that captures the result of func(term) , but this lambda has not been called so far, there is no recursion.

Now the trick in the main should be clearer:

  • terminal(stdout) returns the unnamed lambda that captured stdout.
  • (hello) calls this unnamed lambda, passing as arg hello callable. This is called on a previously recorded stdout. hello(stdout) returns stdout again, which is used as an argument to the terminal, returning another unnamed lambda that fixed stdout.
  • (world) same as 2.
+8
Sep 02 '14 at 11:56 on
source share
  • terminal (stdout) returns a function by calling its function x , with the func parameter. So:

    terminal(stdout) ==> x(func) { return terminal(func(stdout)) };

  • Now the terminal (stdout) (hi) calls the x(hello) function:

    terminal(stdout)(hello) ==> x(hello) { return terminal(hello(stdout)) };

    This causes the hello get function to be called and returns the x function again.

  • Now the terminal (std) (hello) (world) calls the function x(world) :

    terminal(stdout)(hello) ==> x(world) { return terminal(world(stdout)) };

    This causes the world function to be called and returns the x function again. The function x is no longer called because there is no longer a parameter.

+3
03 Sep '14 at 4:29
source share



All Articles