lambda表达式
lambda表达式形式
[capture list](parameters list) -> return type {function body}
capture list 捕获列表是一个lambda所在函数中定义的局部变量的列表。return type、 parametes list 和function body和普通函数一样是返回类型,参数列表,函数体。
我们可以忽略参数列表和返回类型,但是一定要有函数体和捕获列表。
auto f = []{return 42;};
cout << f() << endl;
捕获方式
值捕获
类似于参数传递,变量的捕获方式也可以是拷贝或者引用。
void func1(){
size_t v1 = 42; //局部变量
auto f = [v1]{return v1};
v1 = 0;
auto j = f(); //j等于42
}
引用传递
void func2(){
size_t v1 = 42; //局部变量
auto f2 = [&v1]{return v1};
v1 = 0;
auto j = f2(); //j等于0;f2保存v1的引用。
}
引用捕获和返回引用有着相同的问题,因为lambda捕获的是局部变量,这些变量在函数结束后就不存在了。如果lambda在函数结束后执行,捕获引用的对象已经消失了。
但是引用捕获是必要的,但某个对象无法拷贝时,如os对象(输入输出对象)
隐式捕获
可以让编译器更具lambda体的代码来推断我们需要哪些变量。为了指示编译器推断捕获列表,应在捕获列表中写一个&或=
- & 表示编译器使用的是引用捕获
- = 表示编译器使用的是值捕获
wc = find_if(words.begin(), words.end(),
[=](const string &s)
{return s.size() >= sz;}
该lambda表达式会直接捕获lambda所在函数体里面叫sz的局部变量,并且使用引用捕获。
如果打算一些变量使用值捕获,一些使用引用捕获,那么就需要隐式和显式同时使用。
可变lambda
对于一个被捕获的变量,希望能更改它的值,就必须加入mutable
void func3(){
size_t v1 = 42;
auto f = [v1]()mutable{return v1++};
v1 = 0;
auto j = f(); //j = 43;
}
对于引用捕获的变量也是如此
void func4(){
size_t v1 = 42;
auto f = [&v1]()mutable{return v1++};
v1 = 0;
auto j = f(); //j = 1;
}
指定返回类型
默认情况下,若一个lambda体包含return之外的任何语句,编译器假定该lambda返回void。
现在使用一个标准库的transfrom算法和一个lambda来将一个序列中的每个负数替换成了绝对值。
transfrom(vi.begin(), vi.end(), vi.begin(),[](int i){return i < 0?-i:i});
函数transfrom接受三个迭代器,前两个迭代器表示输入序列,第三个迭代器表示目的地位置。算法对序列的每个元素调用可调用对象。并将结果写道目的地位置。
在上面这个例子,该lambda体是单一的return语句,返回一个条件表达式的结果。无需指定返回类型,便可推断出来。
transfrom(vi.begin(), vi.end(), vi.begin(),[](int i){if(i<0) return -i;else return i});
这个lambda表达式返回的是void类型,但是我们需要返回int类型。
这样的话,就必须使用尾置返回类型
transfrom(vi.begin(), vi.end(), vi.begin(),
[](int i)-> int {if(i<0) return -i;else return i});
lambda优于普通函数的地方
按道理,lambda表达式大都可以被函数替代,那在哪些场景下,必须要用lambda表达式不可。
先讲一下find_if函数
find_fi()函数需要传入一对迭代器,指向的是输入序列。第三个参数是一个要返回bool的函数。
当函数不返回false,即它第一次返回true时,find_if返回该迭代器。
现在想要知道第一个word长度大于3的word
int size = 3;
auto iter = find_if(words.begin(), words.end(),
[size](const string word){ return word.size() > size});
从截图可以看出,该函数只能接受一个参数。
如果要用函数替代lambda ,用下面这个函数是不ok的。
而lambda可以通过捕获size,不使用两个参数,从而解决了这个问题。
bool check_size(const string word, string::size size){
if(word.size() > size){
return true;
}else{
return false;
}
}