1、介绍
lambda表达式常用于向函数传递谓词,有些函数的参数只能接受一元谓词(如find_if),但是如果我们需要向函数传递二元谓词,这时候可以使用lambda表达式(匿名函数),lambda表达式是重载了运算符[]的函数。
2、lambda表达式的使用
一个lambda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数。与函数相似,一个lambda表达式具有一个返回类型、一个参数列表和一个函数体。一个lambda表达式具有如下的形式:
[捕获参数](参数列表) -> 返回类型 {函数体}
lambda表达式必须使用尾置返回类型。
(1)简单的lambda表达式
auto func = [] {return 42;};
上式中忽略了参数列表和返回类型,但是必须包含捕获列表和函数体。
cout <<func() <<endl; //调用func的方式和普通函数类似,此式输出42.
(2)含有参数的lambda表达式
向lambda表达式传递参数的时候,和普通函数类似,但是lambda表达式不能含有默认值,因此传递参数的时候实参与形参一定相等。
完成isShorter函数的lambda表达式如下:
[] (const string& a, const string& b) { return a.size() < b.size(); }
空捕获列表表示lambda表达式不使用其所在函数的局部变量。上述的lambda表达式调用的时候会进行比较两个参数的长短,并返回bool值。
在stable_sort()函数中使用上述的lambda表达式:
stable_sort(words.begin(), words.end(), [] (const string& a, const string& b) { return a.size() < b.size(); });
这样stable_sort函数在排序的时候就会调用lambda表达式,按字符串长度从小到大排列。
注:一个lambda表达式如果如果忽略返回类型,那么lambda表达式会根据函数体的返回值推断类型,但是前提是函数体只有一个return语句;如果lambda表达式忽略了返回类型,并且函数体不有return语句之外的语句,那么返回类型是void。
(3)使用捕获列表的lambda表达式
lambda表示一般是用于函数中,如果lambda表达式需要使用到函数中的局部变量,则需要是用捕获列表进行捕获。
如:使用find_if函数查找第一个满足字符串长度大于len的字符。
int len = 5;
stable_sort(words.begin(), words.end(), [] (const string& a, const string& b) { return a.size() < b.size(); });
find_if(words.begin(), words.end(), [len](const string& a) { return a.size() > len; });
上述例子中find_if函数中的第三个参数传递了一个lambda表达式,该表达式使用捕获列表捕获了参数len,对words中的每一个元素的长度和len进行比较,返回第一个长度大于len的字符串的迭代器。
(4)值捕获与引用捕获
值捕获:
lambda表达式采用[name]的形式捕获参数的时候,便是使用的值捕获。采用值捕获的前提是参数能够拷贝。与函数参数不同,被捕获的变量的值是在lambda表达式创建的时候拷贝的,而不是调用时拷贝。
int i = 40;
auto func = [i] {return i;};
i = 0;
auto j = func(); //此时变量j=40,func保存了我们创建它时i的拷贝
引用捕获:
引用捕获和变量的引用类似,在lambda表达式中采用引用捕获,那么在lambda表达式使用的便是引用所绑定的对象的值。如果采用引用捕获,我们必须确保被引用的对象在lambda表达式执行的时候是存在的。
int i = 40;
auto func = [&i] {return i;};
i = 0;
auto j = func(); //此时变量j=0,func保存的是i的引用,没有拷贝。
隐式捕获:
可以用=和&表示lambda表达式是采用值捕获还是引用捕获
find_if(words.begin(), words.end(), [=](const string& a) { return a.size() > len; });
使用=号表示采用的是值捕获(lambda表达式中所有的参数都是)。
find_if(words.begin(), words.end(), [&](const string& a) { return a.size() > len; });
使用&号表示采用的是引用捕获(lambda表达式中所有的参数都是)。
显式和隐式捕获混用:
for_each(words.begin(), words.end(), [&, c] (const string &a) { os<< a << c; });
上面的lambda表达式中显式声明变量c采用值捕获的方式,其余变量采用引用捕获。
for_each(words.begin(), words.end(), [=, &os] (const string &a) { os<< a << c; });
上面的lambda表达式中显式声明变量os采用引用捕获的方式,其余变量采用值捕获。
(5)指定lambda表达式的返回类型
如前所述,如果要指定lambda表达式的返回值类型,必须使用尾置返回类型。
transform(v.begin(), v.end(), v.begin(), [](int i) { return i < 0 ? i : -i; });
上面的表达式是将数组v中的负数替换为绝对值,因为lambda表达式中只有一个return 语句,因此可以不指定返回类型,编译器会自动推断返回类型为int。
但是若修改成:
transform(v.begin(), v.end(), v.begin(), [](int i) { if(i < 0) return -i; else return i;});
则会发生编译报错。需要采用尾置返回类型修改。
transform(v.begin(), v.end(), v.begin(), [] (int i) -> int { if(i < 0) return -i; else return i;});
这样便指定了lambda表达式的返回类型。
总结:
(1)lambda表达式为匿名函数,常用于在函数中传递谓词,如排序算法中传递是递增排序还是递减排序。
(2)lambda表达式采用引用捕获参数的时候一定要保证在lambda表达式调用参数的时候变量一直存在。
(3)lambda表达式中如果只有一条return语句,返回类型由编译器自动推断;否则默认返回void
(4)要指定lambda表达式的返回类型要用尾置返回类型。
(5)lambda表达式要尽量短!(类似内联函数)