lambda表达式简称lambda式,是C++11 新添加的特性。它适合表达简短、短期调用的可调用对象。
看effective modern c++时,对lambda的简单用法做个记录。
1. lambda原型如下:
[captures](params) mutable exception attribute {body}
其中,
-
captures
是在lambda函数体中可以访问的外部变量,有两种默认捕获模式:按值捕获和按引用捕获 -
params
指定表达式的参数 -
mutable
修饰符表示lambda函数可以修改捕获的变量,并调用对象的非const方法 -
exception
告诉编译器函数是否抛异常(noexcept) -
attribute
用来声明属性
lambda的返回值:如果函数中有return语句,则类型从return后面的表达式推导而来;如果没有return语句,则类似于void。
关于capture
的捕获模式: [a, &b] 表示a按值捕获,b按引用捕获;[]表示不捕获任何外部变量
2. lambda有哪些用法?
- STL中"_if"函数族,比如
std::find_if(container.begin(), container.end(), [](int val){ return val>0})
- 自定义比较函数(std::sort, std::nthelement, std::lower_bound)
- 为
std::unique_ptr, std::shared_ptr
自定义解析器 - 用lambda实现闭包
lambda与闭包
- 闭包是lambda表达式的运行期对象,根据不同的捕获模式,闭包会持有数据的副本或引用。在上面find_if函数中,闭包就是作为第三个实参在运行期传递给find_if函数的。
- 闭包类就是实例化闭包的类。每个lambda表达式都会触发编译器生成独一无二的闭包类。而闭包中的语句会变成它的闭包类成员函数的可执行指令。
就存在期而言,lambda和闭包类存在于编译期,闭包存在于运行期。
闭包属于可调用对象,因此可以像变量一样对它赋值。
lambda的简单使用
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
int main()
{
vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int threshold = 5;
// 删除小于threshold的元素
std::remove_if(v.begin(), v.end(), [&threshold](int v){return v<threshold;});
for_each(v.begin(), v.end(), [](int v){std::cout<<v<<"\t";});
// lambda赋给变量, 只能用auto修饰
auto f1 = [](int v){ return v*2;};
// 由于lambda也是一种可调用对象
// 因此,也可以用function来接收
std::function<int(int)> f2 = [](int v){return v+1;};
std::cout<<f2(2)<<std::endl;
}
引用悬挂问题
默认按引用捕获的一个可能问题是, 引用悬挂。即按引用捕获会导致包含指向局部变量的引用,当lambda离开局部作用域时,会导致引用的那个变量被释放,lambda里的那个引用发生"悬挂"。
using func_v = std::vector<std::function<bool(int)>>;
func_v filters;
...
void addFilters()
{
int threshold = 5; // 局部变量
filters.emplace_back([&threshold](int v){return v<threshold;});
}// threshold已经被销毁,但是filters中仍保留着对它的引用
filters.doSomething(); // 未定义行为
对于上面这个问题来说,传值足以解决问题。但这并不表示,按值传递就是安全的,因为传递指针也是按值传递的一种。
当我们把一个指针(按值传递)传递给lambda(其实是所有可调用对象)时,你不知道当你在这个函数里兴高采烈地大展拳脚时,在函数外面可能某个”不良分子“把指针指向的对象悄悄地释放掉了。这段话是为了说明按值捕获也许并没有想象中比按引用捕获安全到哪里去。
那么怎么解决这个问题呢?C++14提供了广义lambda捕获
,它将你想要捕获的变量做一个副本,然后在lambda里使用这个副本。
void addFilters()
{
int threshold = 5; // 仍是局部变量
filters.emplace_back([threshold = threshold](int v){return v<threshold;});
// [threshold = threshold]将threshold复制入闭包,lambda内部使用副本
}
网友评论