C++中的Lambda-Over-Lambda 14

P0W*_*P0W 69 c++ lambda c++14

以下递归lambda调用如何结束/终止?

#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;

}
Run Code Online (Sandbox Code Playgroud)

我在这里错过了什么?

Running code

Nim*_*Nim 45

这不是一个递归函数调用,一步一步看一下:

  1. terminal(stdout) - 这只是返回已捕获的lambda stdout
  2. 使用lambda调用1.的结果,lambda hello执行lambda(func(term)),其结果传递给terminal()它,它只返回lambda,如1所示.
  3. 使用lambda调用2.的结果world,它与2相同,这次返回值被丢弃...


Mik*_*our 26

调用本身不是递归的.它返回一个函数对象,如果被调用,它将terminal再次调用以生成另一个函数对象.

因此terminal(stdout)返回一个捕获的函子,stdout可以用另一个函数对象调用.再次调用它,用捕获的术语(hello)调用hello仿函数stdout,输出"Hello"; 调用terminal并返回另一个函子,这次捕获的返回值hello- 仍然是stdout.调用该仿函数,(world)再次输出相同的函数"World".


Mar*_* A. 13

这里的关键是要明白这是有效的:

world(hello(stdout));
Run Code Online (Sandbox Code Playgroud)

并将打印"Hello World".递归的lambda系列可以展开为

#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;

}
Run Code Online (Sandbox Code Playgroud)

Coliru的例子


Pio*_*cki 10

它可以在内部翻译成如下内容:

#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);
}
Run Code Online (Sandbox Code Playgroud)

LIVE DEMO

实际上这就是编译器 lambdas 场景背后所做的事情(有一些近似).


Dar*_*ioP 8

我认为混淆的来源是将lambda声明读作lambda调用.确实在这里:

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

作者刚刚声明了一个lambda terminal,它接受一个任意参数term并返回一个未命名的lambda,仅此而已!让我们来看看这个未命名的lambda,它:

  • 接受一个可调用对象func作为参数并调用它在副本上捕获的参数term
  • 返回调用结果的终端结果func(term); 所以它返回另一个未命名的lambda来捕获结果func(term),但是这个lambda现在不被调用,没有递归.

现在主要的诀窍应该更清楚:

  1. terminal(stdout) 返回一个未命名的lambda,它捕获了stdout.
  2. (hello)将此未命名的lambda称为arg hello callable.这在先前捕获的stdout上被调用.hello(stdout)再次返回stdout,它被用作对终端的调用的参数,返回另一个未命名的lambda,它捕获了stdout.
  3. (world) 与2相同.