Lambda表达式
lambda 表达式的概念和基本用法
lambda表达式有如下优点:
- 声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序,好的可读性和可维护性。
- 简洁:不需要额外再写一个函数或者函数对象,避免了代码膨胀和功能分散,让开发者更加集中精力在手边的问题,同时也获取了更高的生产率。
- 在需要的时间和地点实现功能闭包,使程序更灵活。
lambda表达式用法:
[ capture ] ( params ) opt -> ret { body; };
lambda表达式示例:
auto f = [](int *a*) -> int { return *a* + 1; };
cout << f(1) << endl;
C++11 中允许省略 lambda 表达式的返回值定义。
auto f = [](int *a*) { return *a* + 1; };
cout << f(1) << endl;
- 初始化列表不能用于返回值的自动推导,需要显式给出具体的返回值类型。
- lambda 表达式在没有参数列表时,参数列表是可以省略的。
使用 lambda 表达式捕获列表
lambda 表达式还可以通过捕获列表捕获一定范围内的变量:
- [] 不捕获任何变量。
- [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
- [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
- [=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。
- [bar] 按值捕获 bar 变量,同时不捕获其他变量。
- [this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 & 或者 =,就默认添加此选项。捕获 this 的目的是可以在 lamda 中使用当前类的成员函数和成员变量。
lambda表达式易错点:
int a = 0;
auto f = [=](){return a;};
a += 1;
cout << f() << endl;
如果希望去修改按值捕获的外部变量应当怎么办呢?这时,需要显式指明 lambda 表达式为 mutable:
int a = 0;
auto f1 = [=]{ return a++; };
auto f2 = [=]() mutable { return a++; };
lambda 表达式的类型
“闭包类型(Closure Type)”。它是一个特殊的,匿名的非 nunion 的类类型。
对于没有捕获任何变量的 lambda 表达式,还可以被转换成一个普通的函数指针。
lambda 表达式可以说是就地定义仿函数闭包的“语法糖”。它的捕获列表捕获住的任何外部变量,最终均会变为闭包类型的成员变量。而一个使用了成员变量的类的 operator(),如果能直接被转换为普通的函数指针,那么 lambda 表达式本身的 this 指针就丢失掉了。而没有捕获任何外部变量的 lambda 表达式则不存在这个问题。
这里也可以很自然地解释为何按值捕获无法修改捕获的外部变量。因为按照 C++ 标准,lambda 表达式的 operator() 默认是 const 的。一个 const 成员函数是无法修改成员变量的值的。而 mutable 的作用,就在于取消 operator() 的 const。
需要注意的是,没有捕获变量的 lambda 表达式可以直接转换为函数指针,而捕获变量的 lambda 表达式则不能转换为函数指针。
声明式的编程风格,简洁的代码
lambda 表达式的价值在于,就地封装短小的功能闭包,可以极其方便地表达出我们希望执行的具体操作,并让上下文结合得更加紧密。
|