一:Lambda表达式概述
- Lambda表达式是现代C++在C++11和更高版本中的一个新语法糖,在C++11,C++14,C++17和C++20中Lambda表达的内容还在不断更新。
- Lambda表达式(也称为lambda函数)是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。
- 通常,lambda用于封装传递给算法或异步方法的几行代码。
二:lambda表达式示例
Lambda有很多种叫法,有Lambda表达式,Lambda函数,匿名函数,本文为了方便表述统一用Lambda表达式叙述。下面是一个Lambda表达式实例。
#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned n)
{
std::sort(x,x+n),
// Lambda expression begins
[](float a, float b) {
return (std::abs(a)< std::abs(b));
}
// Lambda expression end
}
在上面的实例中,std::sort 函数第三个参数应该是传递一个 排序规则的函数,但是这个实例中直接将排序函数的 实现:写在了应该传递函数的位置,省去了定义排序函数的过程,对于这种不需要复用且短小的函数,直接传递函数体可以增加代码的可读性。
三:Lambda表达式语法定义
1:捕获列表:在C++规范中也称为Lambda导入器,捕获列表总是出现在lambda函数的开始处。实际上,[] 是lambda引出符,编译器根据该引出符判断接下来的代码是 lambda函数,捕获列表能够捕捉上下文的变量,以供Lambda函数使用。
2:参数列表:与普通函数的参数列表一致,如果不需要参数传递,则可以连同括号 “()” 一起省略
3:可变规格:mutable修饰符,默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可以省略(即使参数为空)
4:异常说明:用于lambda表达式内部函数抛出的异常。
5:返回类型:lambda表达式的函数体返回类型我们可以在不需要返回值得时候可以连同 “->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
说明:lambda表达式的返回类型会自动推导。除非指定了返回类型,否则不必使用关键字。返回类型类似于通常的方法或函数的返回类型部分。但是返回类型必须在参数列表之后,并且必须在返回类型 -> 之前包含类型关键字。如果lambda主体仅包含一个 return语句或者该表达式返回值,那么可以省略Lambda表达式的 return-type 部分,如果Lambda主体包含一个 return语句,则编译器 将从 return表达式的类型中推导出 return类型。否则: 编译器将返回类型推导为 void .
auto x = [ ](int i) {return i ;}
6:lambda函数体:与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
四:Lambda表达式的优缺点
优点
- 可以直接在需要调用函数的位置定义短小精悍的函数,而不需要预先定义好函数
- 使用lambda表达式变得更加紧凑,结构层次更加明显,代码可读性好
例如:
std::find_if(v.begin(),v.end(),[](int& item) {return item >2} ) ;
缺点:
- lambda表达式语法比较灵活,增加了阅读代码的难度
- 对于函数复用无能为力
五:lambda表达式工作原理
编译器会把一个Lambda表达式生成一个匿名类的 匿名对象,并在类中重载函数调用运算符,实现 operator()方法。
auto print = [] {cout << " hello world" << endl ; }
编译器会把上述代码翻译为下面代码
class print_class
{
public:
void operator()(void) const {
cout << "hello world" << endl;
}
};
// 用构造的类创建对象,print 此时就是 一个函数对象
auto print = print_class();
事实上,编译器这么翻译和仿函数基本是一致的。
#include<iostream>
#include<string>
using namespace std;
class Functor
{
public:
void operator()(const string& str) const
{
cout<< str << endl;
}
}
int main(){
Functor myFunctor;
myFunctor("hello");
return 0;
}
六: lambda表达式举例
6.1:lambda表达式被:函数指针,auto, function等方式接收
#include<iostream>
#include<string>
#include<functional>
#include "lambda_.h"
using namespace std;
typedef void (*p_nameFunc)(string name);
void use_lambda1() {
// 1:匿名调用
[](string name) {
cout << "this is anonymous" << endl;
cout << "hello" << endl;
}("jack ma");
// 2:通过auto赋值
auto fname = [](string name)->string {
cout << "this is auto" << endl;
cout << "hello" << endl;
return "hello world";
};
fname("Robin ma");
// 函数指针
p_nameFunc fname2 = [](string name)
{
cout << "this is P_nameFunc" << endl;
cout << "hello " << name << endl;
};
fname2("liu");
// 4:function
function<void(string)> funcName;
funcName = [](string name)
{
cout << "this is function" << endl;
cout << "hello " << name << endl;
};
funcName("wang");
}
int main() {
use_lambda1();
}
6.2:lambda捕获方式
void use_lambda2() {
int age = 33;
int score = 100;
string name = "jack";
string job = "softengineer";
// 值捕获
[age, name](string name_)
{
cout << "age is: " << age << " name is: " << name << " self-name is: " << name_ << endl;
// name = "xiaoli"; // error : 表达式必须是可修饰的左值
// age = 20; // error : 表达式必须是可修饰的左值
}("vivo");
// 引用捕获
[&age, &name](string name_)
{
cout << "age is: " << age << " name is : " << name << "self-name is: " << name_ << endl;
name = "xiaoli";
age = 20;
}("huawei");
// 全部用值捕获,name用应用捕获
[=, &name]()
{
cout << "age is: " << age << " name is: " << name << " score is: " << score << " job is: " << job << endl;
name = "cui hua";
}();
// 全部用引用捕获,只有name 用值捕获
[&, name]()
{
cout << "age is: " << age << " name is: " << name << " score is: " << score << " job is: " << job << endl;
}();
}