• C++ 闭包(closure)


    闭包有很多种定义,一种说法是,闭包是带有上下文的函数。说白了,就是有状态的函数。更直接一些,不就是个类吗?换了个名字而已。

    一个函数, 带上了一个状态, 就变成了闭包了. 什么叫 "带上状态" 呢? 意思是这个闭包有属于自己的变量, 这些个变量的值是创建闭包的时候设置的, 并在调用闭包的时候, 可以访问这些变量.

    函数是代码, 状态是一组变量 ,将代码和一组变量捆绑 (bind) , 就形成了闭包 ,内部包含 static 变量的函数, 不是闭包, 因为这个 static 变量不能捆绑. 你不能捆绑不同的 static 变量. 这个在编译的时候已经确定了.

    闭包的状态捆绑, 必须发生在运行时.

    闭包的实现

    C++ 里使用闭包有3个办法

    重载 operator()

    因为闭包是一个函数+一个状态, 这个状态通过 隐含的 this 指针传入. 所以 闭包必然是一个函数对象. 因为成员变量就是极好的用于保存状态的工具, 因此实现 operator() 运算符重载, 该类的对象就能作为闭包使用. 默认传入的 this 指针提供了访问成员变量的途径.

    事实上, lambda 和 bind 的原理都是这个.

    class MyFunctor
    {
    public:
    MyFunctor(float f) : round(f) {}
    int operator()(float f) { return f + round; }
    private:
    float round;
    };
    float round = 0.5;
    MyFunctor f(round);

    lambda

    c++11 里提供的 lambda表达式就是很好的语法糖. 其本质和手写的函数对象没有区别.

    float round = 0.5;
    auto f = [=](float f) { return f + round; }

    boost::bind/std::bind

    标准库提供的 bind 是更加强大的语法糖, 将手写需要很多很多代码的闭包, 浓缩到一行 bind 就可以搞定了.

    int boost_func(float f, float round)
    { return f + round; }
    float round = 0.5;
    boost::function<int(float)> f = boost::bind(boost_func, _1, round);

    比较上面三种方式,有一些细节需要注意:

    1. closure的状态特指其运行的上下文。 closure将存贮它运行时需要的上下文,从而保证在closure创建时的上下文可以在closure运行时依然有效。

    比如round就是closure的上下文。保存上下文的这一特点通常被称作“capture”或者是"bind"。 capture可以自己写,比如MyFuctor f(round); 也可以用boost::bind。

    当然最方便的还是让编译器帮你自动完成。编译器将自动识别closure用到的变量,然后创建一个匿名的类,将这个变量保存到匿名类的成员变量中。

    C++中有两种capture方式,by value和by reference。写法是[=]和[&]。

    需要注意的是,capture by reference是不会修改被capture变量的生命周期的,你要保证被capture的变量在closure运行时是有效的。

    这一点不像Java,Java中变量被capture的话,就变成被引用了,从而GC不会回收它。

    2. closure的类型是隐藏的,每次创建一个closure,编译器都会创建一个新的类型。

    如果你想保存一个clousre时就不是那么直接,因为你不知道它的类型。这时那需要一些模板技巧,可参考boost::function的实现。

    简单的方式是直接用std::function来保存。

    std::function<int(float)> closure;

    closure = [](float f) { return 0.0f };

    closure = [](float f) { return 1.0f };

  • 相关阅读:
    HTML解决浏览器字体大小12px限制,实现自动适应大小
    Oracle 大最插入数据 一段时间之后变慢问题解决方法
    中间件使用-nginx 中ssl证书的设置
    asp.net core学习:准备asp.net core源码编译环境
    批量修改文件名后缀
    tcpdump 抓所有网卡的包
    mysql数据库备份
    x64架构下Linux系统函数调用
    博客背景美化——动态雪花飘落
    MySQL锁:03.InnoDB行锁
  • 原文地址:https://www.cnblogs.com/Aion/p/3449756.html
Copyright © 2020-2023  润新知