Lambda表达式
前言auto关键字
auto(C++11新特性)
C++11赋予了auto新的定义,让其做自动类型推导,也就是说,编译器可以在编译期间自动推算出变量的类型,这样就可以更加方便的编写代码了。
使用:
- auto 用法最常见的场景是声明变量时,让编译器根据初始化表达式自动推导出变量的类型。
- 在使用 STL 容器(如 std::vector、std::map 等)时,auto 能大大简化代码,尤其是在使用迭代器时。
1
2
3
4
5
6
7
8
9
10
11
12
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用 auto 推导类型,避免手动指定迭代器类型
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
return 0;
} - C++11 引入了范围-based for 循环(range-based for loop),auto 关键字与之结合可以使代码更简洁,尤其是在处理容器元素时。
1
2
3
4
5std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用 auto 自动推导类型来遍历容器元素
for (auto element : vec) {
std::cout << element << " ";
} - 与lambda配合使用。
注意:
- auto 不能在函数的参数中使用。
- auto 关键字不能定义数组
auto(C++14新特性)
C++14中auto可以作为返回值了
C++ Lambda表达式(C++11新特性)
Lambda表达式:是 C++11引入的一种函数对象,可以方便地创建匿名函数。与传统的函数不同,Lambda表达式可以在定义时直接嵌入代码,无需单独定义函数名称、参数和返回类型等信息。Lambda表达式通常用于需要定义一些简单的回调函数或者函数对象。
什么是 Lambda表达式
Lambda表达式是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象(闭包)的简便方法。Lambda表达式的基本语法如下:1
[capture list] (parameter list) -> return type { function body }
其中:
- capture list 是捕获列表,用于指定 Lambda表达式可以访问的外部变量,以及是按值还是按引用的方式访问。捕获列表可以为空,表示不访问任何外部变量,也可以使用默认捕获模式 & 或 = 来表示按引用或按值捕获所有外部变量,还可以混合使用具体的变量名和默认捕获模式来指定不同的捕获方式。
- parameter list 是参数列表,用于表示 Lambda表达式的参数,可以为空,表示没有参数,也可以和普通函数一样指定参数的类型和名称,还可以在 c++14 中使用 auto 关键字来实现泛型参数。
- return type 是返回值类型,用于指定 Lambda表达式的返回值类型,可以省略,表示由编译器根据函数体推导,也可以使用 -> 符号显式指定,还可以在 c++14 中使用 auto 关键字来实现泛型返回值。
function body 是函数体,用于表示 Lambda表达式的具体逻辑,可以是一条语句,也可以是多条语句,还可以在 c++14 中使用 constexpr 来实现编译期计算。
return type一般省略,所以lambda表达式一般式这个形式
1
[capture list] (parameter list) { function body }
理解
首先例如1
2
3
4[]
{
cout << "hello lambda" << endl;
}这是一个最简单的lambda表达式,在它后面加上()就可以调用它。
但是,不会这么用,给它一个名字,由于不知道什么类型,所以用auto1
2
3
4auto L= []
{
cout << "hello lambda" << endl;
};使用L()就可以调用它了。
lambda表达式的捕获方法
- 值捕获(capture by value):在捕获列表中使用变量名,表示将该变量的值拷贝到 Lambda 表达式中,作为一个数据成员。值捕获的变量在 Lambda 表达式定义时就已经确定,不会随着外部变量的变化而变化。值捕获的变量默认不能在 Lambda 表达式中修改,除非使用
mutable
关键字。
例如1
2
3
4int x = 10;
auto f = [x] (int y) { return x + y; }; // 值捕获 x
x = 20; // 修改外部的 x
cout << f(5) << endl; // 输出 15,不受外部 x 的影响 - 引用捕获(capture by reference):在捕获列表中使用
&
加变量名,表示将该变量的引用传递到 Lambda 表达式中,作为一个数据成员。引用捕获的变量在 Lambda 表达式调用时才确定,会随着外部变量的变化而变化。引用捕获的变量可以在 Lambda 表达式中修改,但要注意生命周期的问题,避免悬空引用的出现。1
2
3
4int x = 10;
auto f = [x] (int y) { return x + y; }; // 值捕获 x
x = 20; // 修改外部的 x
cout << f(5) << endl; // 输出 25,受外部 x 的影响 - 隐式捕获(implicit capture):在捕获列表中使用 = 或 &,表示按值或按引用捕获 Lambda 表达式中使用的所有外部变量。这种方式可以简化捕获列表的书写,避免过长或遗漏。隐式捕获可以和显式捕获混合使用,但不能和同类型的显式捕获一起使用。
1
2
3
4
5
6int x = 10;
int y = 20;
auto f = [=, &y] (int z) { return x + y + z; }; // 隐式按值捕获 x,显式按引用捕获 y
x = 30; // 修改外部的 x
y = 40; // 修改外部的 y
cout << f(5) << endl; // 输出 55,不受外部 x 的影响,受外部 y 的影响
- 值捕获(capture by value):在捕获列表中使用变量名,表示将该变量的值拷贝到 Lambda 表达式中,作为一个数据成员。值捕获的变量在 Lambda 表达式定义时就已经确定,不会随着外部变量的变化而变化。值捕获的变量默认不能在 Lambda 表达式中修改,除非使用
- lambda表达式的使用
- 定义简单的匿名函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using namespace std;
int main()
{
// 定义一个 Lambda表达式,计算两个数的和
auto plus = [] (int a, int b) -> int { return a + b; };
// 调用 Lambda表达式
cout << plus(3, 4) << endl; // 输出 7
// 定义一个 Lambda表达式,判断一个数是否为奇数
auto is_odd = [] (int n) { return n % 2 == 1; };
// 调用 Lambda表达式
cout << is_odd(5) << endl; // 输出 1
cout << is_odd(6) << endl; // 输出 0
return 0;
} - 捕获外部变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using namespace std;
int main()
{
int x = 10;
int y = 20;
// 定义一个 Lambda表达式,按值捕获 x 和 y
auto add = [x, y] () -> int { return x + y; };
// 调用 Lambda表达式
cout << add() << endl; // 输出 30
// 修改 x 和 y 的值
x = 100;
y = 200;
// 再次调用 Lambda表达式
cout << add() << endl; // 输出 30,捕获的是 x 和 y 的副本,不受外部变化的影响
// 定义一个 Lambda表达式,按引用捕获 x 和 y
auto mul = [&x, &y] () -> int { return x * y; };
// 调用 Lambda表达式
cout << mul() << endl; // 输出 20000
// 修改 x 和 y 的值
x = 1000;
y = 2000;
// 再次调用 Lambda表达式
cout << mul() << endl; // 输出 2000000,捕获的是 x 和 y 的引用,会反映外部变化的影响
return 0;
} - Lambda表达式作为函数参数
- Lambda表达式作为函数返回值
- 定义简单的匿名函数
- lambda表达式的实质
Lambda表达式虽然是一种语法糖,但它本质上也是一种函数对象,也就是重载了 operator() 的类的对象。每一个 Lambda表达式都对应一个唯一的匿名类,这个类的名称由编译器自动生成,因此我们无法直接获取或使用。Lambda表达式的捕获列表实际上是匿名类的数据成员,Lambda表达式的参数列表和返回值类型实际上是匿名类的 operator() 的参数列表和返回值类型,Lambda表达式的函数体实际上是匿名类的 operator() 的函数体。
例如1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18int x = 10;
auto f = [x] (int y) -> int { return x + y; };
//相当于定义了一个匿名类,类似于:
int x = 10;
class __lambda_1
{
public:
__lambda_1(int x) : __x(x) {} // 构造函数,用于初始化捕获的变量
int operator() (int y) const // 重载的 operator(),用于调用 Lambda表达式
{
return __x + y; // 函数体,与 Lambda表达式的函数体相同
}
private:
int __x; // 数据成员,用于存储捕获的变量
};
auto f = __lambda_1(x); // 创建一个匿名类的对象,相当于 Lambda表达式lambda(C++14)
- 初始化捕获(init capture):C++14 引入的一种新的捕获方式,它允许在捕获列表中使用初始化表达式,从而在捕获列表中创建并初始化一个新的变量,而不是捕获一个已存在的变量。这种方式可以使用 auto 关键字来推导类型,也可以显式指定类型。这种方式可以用来捕获只移动的变量,或者捕获 this 指针的值。
1
2
3
4int 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:C++14 允许在 Lambda表达式的参数列表和返回值类型中使用 auto 关键字,从而实现泛型 Lambda,即可以接受任意类型的参数和返回任意类型的值的 Lambda表达式。
lambda(C++17)
- 捕获 this 指针:C++17 允许在 Lambda表达式的捕获列表中使用 *this,从而实现捕获 this 指针,即可以在 Lambda表达式中访问当前对象的成员变量和成员函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 t(10); // 创建一个 Test 对象
t.show(); // 调用成员函数,输出 10
t.add(5); // 调用成员函数,输出 15
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 十柒!
评论