在本文中,我们将介绍 c++ Lambda表达式的定义、用法、特点和原理
通过多个例子,展示了 Lambda表达式的参数、返回值、捕获、引用、修改等方式,以及如何用它们定义匿名函数和算法。
Lambda表达式是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象(闭包)的简便方法。Lambda表达式的基本语法如下:
[capture list] (parameter list) -> return type { function body }
其中:
int x = 10;
auto f = [x] (int y) -> int { return x + y; }; // 值捕获 x
x = 20; // 修改外部的 x
cout << f(5) << endl; // 输出 15,不受外部 x 的影响
int x = 10;
auto f = [&x] (int y) -> int { return x + y; }; // 引用捕获 x
x = 20; // 修改外部的 x
cout << f(5) << endl; // 输出 25,受外部 x 的影响
=
或 &
,表示按值或按引用捕获 Lambda 表达式中使用的所有外部变量。int x = 10;
int y = 20;
auto f = [=, &y] (int z) -> int { return x + y + z; }; // 隐式按值捕获 x,显式按引用捕获 y
x = 30; // 修改外部的 x
y = 40; // 修改外部的 y
cout << f(5) << endl; // 输出 55,不受外部 x 的影响,受外部 y 的影响
int x = 10;
auto f = [z = x + 5] (int y) -> int { return z + y; }; // 初始化捕获 z,相当于值捕获 x + 5
x = 20; // 修改外部的 x
cout << f(5) << endl; // 输出 20,不受外部 x 的影响
Lambda表达式相比于普通函数和普通类,有以下几个优点:
Lambda表达式的缺点
我们可以使用 Lambda表达式作为函数的参数,这样可以方便地定义和传递一些简单的函数对象,例如自定义排序规则、自定义比较函数等。例如:
#include
#include
#include
using namespace std;
// 定义一个结构体
struct Item
{
Item(int aa, int bb) : a(aa), b(bb) {}
int a;
int b;
};
int main()
{
vector<Item> vec;
vec.push_back(Item(1, 19));
vec.push_back(Item(10, 3));
vec.push_back(Item(3, 7));
vec.push_back(Item(8, 12));
vec.push_back(Item(2, 1));
// 使用 Lambda表达式,根据 Item 中的成员 a 升序排序
sort(vec.begin(), vec.end(), [] (const Item& v1, const Item& v2) { return v1.a < v2.a; });
// 使用 Lambda表达式,打印 vec 中的 Item 成员
for_each(vec.begin(), vec.end(), [] (const Item& item) { cout << item.a << " " << item.b << endl; });
return 0;
}
我们可以使用 Lambda表达式作为函数的返回值,这样可以方便地定义和返回一些简单的函数对象,例如工厂函数、闭包函数等。例如:
#include
using namespace std;
// 定义一个函数,返回一个 Lambda表达式,实现两个数的加法
auto make_adder(int x)
{
return [x] (int y) -> int { return x + y; };
}
int main()
{
// 调用函数,得到一个 Lambda表达式
auto add5 = make_adder(5);
// 调用 Lambda表达式
cout << add5(10) << endl; // 输出 15
return 0;
}
lambda 表达式会被编译器转换为类(一个匿名类的匿名对象),这个类的名称由编译器自动生成,我们无法直接获取或使用,并在类中重载函数调用运算符,详情见下:
// 编译器处理前代码
#include
using namespace std;
int main()
{
int a = 10;
auto addFun = [a](int b) -> int
{ return a + b; };
addFun(5);
return 0;
}
//编译器处理后代码
#include
using namespace std;
int main()
{
int a = 10;
// 定义一个匿名类
class __lambda_7_19
{
public:
inline /*constexpr */ int operator()(int b) const // 重载函数调用运算符()
{
return a + b;
}
private:
int a;
public:
__lambda_7_19(int &_a) // 构造函数
: a{_a}
{
}
};
// 创建匿名对象
__lambda_7_19 addFun = __lambda_7_19{a};
addFun.operator()(5);
return 0;
}
lamda 匿名函数的实现原理,就是编译器将会生成一个类,该类的构造函数的参数为 lamda 函数的捕获参数;该类的函数调用操作符函数的参数为 lamda 函数的参数列表;函数调用操作符函数的函数体为 lamda 函数体
auto
关键字,从而实现泛型 Lambda,即可以接受任意类型的参数和返回任意类型的值的 Lambda表达式。*this
,从而实现捕获 this 指针,即可以在 Lambda表达式中访问当前对象的成员变量和成员函数。#include
using namespace std;
// 定义一个类
class Test
{
public:
Test(int n) : num(n) {} // 构造函数,初始化 num
void show() // 成员函数,显示 num
{
cout << num << endl;
}
void add(int x) // 成员函数,增加 num
{
// 定义一个 Lambda表达式,捕获 this 指针
auto f = [*this] () { return num + x; };
// 调用 Lambda表达式
cout << f() << endl;
}
private:
int num; // 成员变量,存储一个整数
};
int main()
{
Test test(10); // 创建一个 Test 对象
test.show(); // 调用成员函数,输出 10
test.add(5); // 调用成员函数,输出 15
return 0;
}