CPP

C++ 闭包的理解

2018-09-26  本文已影响61人  顽强的猫尾草

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

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

函数是代码,状态是一组变量,将代码和一组变量捆绑,就形成了闭包,内部包含 static 变量的函数不是闭包,因为这个 static 变量不能捆绑。闭包的状态捆绑,必须发生在运行时。

闭包的实现

重载 operator()

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

#include <iostream>
using namespace std;

class MyFunctor {
public:
    MyFunctor(int tmp) : round(tmp) {}
    int operator()(int tmp) { return tmp + round; }
private:
    int round;
};

int main() {
    int round = 2;
    MyFunctor f(round);    // 调用构造函数
    cout << "result = " << f(1) << endl;    // operator()(int tmp)
    return 0;
}

输出:

result = 3

lambda 表达式

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

lambda 表达式完整的声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }

各项具体含义如下:

  • capture list:捕获外部变量列表
  • params list:形参列表
  • mutable 指示符:用来声明是否可以修改捕获的变量
  • exception:异常设定
  • return type:返回类型
  • function body:函数体

示例代码:

int main() {
    int round = 2;
    auto f = [=](int f) -> int { return f + round; } ;
    cout << "result = " << f(1) << endl;
    return 0;
}    

输出:

result = 3

隐式捕获
为了指示编译器推断捕获列表,我们可以在捕获列表中写一个 &=& 告诉编译器采用引用捕获,= 则为值捕获。

std::bind

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

#include <iostream>
#include <functional>
using namespace std;

int func(int tmp, int round) {
    return tmp + round;
}

int main()
{
    using namespace std::placeholders;    // adds visibility of _1, _2, _3,...

    int round = 2;
    std::function<int(int)> f = std::bind(func, _1, round);
    cout << "result = " << f(1) << endl;
    return 0;
}

输出:

result = 3

参考:
[1] C++ 闭包(closure)
[2] C++ 11 Lambda表达式
[3] 泛型--lambda表达式捕获

上一篇 下一篇

猜你喜欢

热点阅读