【CPP】Lambda表达式

概述:lambda表达式使用及说明

[toc]

lambda表达式说明

lambda表达式的结构如下所示:

1
auto func = [capture] (params) opt -> ret { func_body; };

其中 func 是可以当作 lambda 表达式的名字,作为一个函数使用,capture 是捕获列表,params 是参数表,opt: 不需要可以省略。 (mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw ()), ret 是返回值类型,func_body 是函数体。

1
2
auto f = [](int a) -> int { return a + 1; };
std::cout << f(1) << std::endl; // 输出: 2

使用 lambda 表达式捕获列表

lambda 表达式还可以通过捕获列表捕获一定范围内的变量:

  • [] 不捕获任何变量。
  • [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
  • [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
  • [=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。
  • [bar] 按值捕获 bar 变量,同时不捕获其他变量。
  • [this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 & 或者 =,就默认添加此选项。捕获 this 的目的是可以在 lamda 中使用当前类的成员函数和成员变量。

捕获列表使用实例

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
#include <iostream>
#include <functional>
using namespace std;

class lambdaTest
{
public:
void output(int x, int y)
{
//auto x1 = [] {return m_number; }; // error

auto x2 = [=] {return m_number + x + y; }; // ok
// auto x3 = [=] { x = 2; return m_number + x + y; }; // error

auto x4 = [&] {return m_number + x + y; }; // ok

auto x5 = [this] {return m_number; }; // ok
//auto x6 = [this] {return m_number + x + y; }; // error
auto x7 = [this, x, y] {return m_number + x + y; }; // ok
}

int m_number = 100;
};

int main()
{
lambdaTest test;

test.output(1,2);
}

函数 是否可读 是否可修改 说明
x1 错误,没有捕获外部变量,不能使用任何外部变量
x2 ✔️ 正确,值传递,可以访问所有外部变量
x3 ✔️ 错误,值传递,右值可读不可修改
x4 ✔️ ✔️ 正确,引用传递,可读可修改
x5 ✔️ this指针的成员变量可读
❌ 其他变量不可读
✔️ this指针的成员可写
❌ 其他变量不可写
正确,捕获this指针,可访问this指针的成员
x6 x5 x5 错误,捕获 this 指针,xy 不是其成员变量,不可访问
x7 ✔️ 值传递三个变量,均可读 ✔️ this指针成员可修改
❌ x 和 y 不可修改
正确,捕获 this 指针,x,y

返回值

lambda表达式的返回值通过 ‘->’ 传递,如下所示:

1
2
3
auto add = [](int x, int y) -> int {
return x + y;
};

如上所示的返回类型还可以使用 decltype 推导返回类型:

decltype 是一个 C++ 关键字,它用于在编译时推断表达式的类型。这在编写通用代码时非常有用,特别是在使用 C++11 引入的 auto 关键字和范围 for 循环时。

1
2
3
auto add = [](int x, int y) -> decltype(x+y) {
return x + y;
};

说明:decltype(a + b) 推导了 a + b 的结果类型,并将该类型作为 lambda 函数的返回类型。

甚至,从 C++14 开始,还可以使用更简洁的方式来推导 lambda 函数的返回类型,即使用 auto 关键字。例如:

1
auto myLambda = [](int a, int b) -> auto { return a + b; };

需要注意的是,使用 auto 推导返回类型的 lambda 函数在某些情况下可能会引发歧义。例如,如果你在 lambda 表达式中返回一个嵌套类型(如 std::pair),auto 可能会被推导为 std::pair,而不是你期望的嵌套类型的具体类型。在这种情况下,使用 decltype 可以更明确地指定返回类型。

其他关键字

使用 lambda 表达式捕获列表捕获外部变量,如果希望去修改按值捕获的外部变量,那么应该如何处理呢?这就需要使用 mutable 选项,被mutable修改是lambda表达式就算没有参数也要写明参数列表,并且可以去掉按值捕获的外部变量的只读(const)属性。

mutable

1
2
3
int a = 0;
auto f1 = [=] {return a++; }; // error, 按值捕获外部变量, a是只读的
auto f2 = [=]()mutable {return a++; }; // ok

💁‍♂️ 注意:mutable 只是允许在函数内部修改值而已,作用域仅限于 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
void lambdaFunc()
{
auto add1 = [](int x, int y) -> int { return x + y; };
auto add2 = [](int x, int y) -> decltype(x + y) { return x + y; };
auto add3 = [](int a, int b) -> auto { return a + b; };

auto modify_add = [](int a, int b) mutable -> auto { a = 3; return a + b; };
auto value_modify_add = [=](int a, int b) mutable -> auto { a = 4; return a + b; };
auto refrence_modify_add = [&](int a, int b) mutable -> auto { a = 5; return a + b; };
auto refrence_modify_add_byRef = [&](int& a, int b) mutable -> auto { a = 6; return a + b; };


int x = 2, y = 2;

cout << "add1:" << add1(x, y) << endl; // add1:4
cout << "add2:" << add2(x, y) << endl; // add2:4
cout << "add3:" << add3(x, y) << endl; // add3:4

cout << "modify_add:" << value_modify_add(x, y) << endl; // modify_add:5
cout << "x=" << x << endl; // x=2
cout << "value_modify_add:" << value_modify_add(x, y) << endl; // value_modify_add:6
cout << "x=" << x << endl; // x=2
cout << "value_modify_add:" << refrence_modify_add(x, y) << endl; // value_modify_add:7
cout << "x=" << x << endl; // x=2
cout << "refrence_modify_add_byRef:" << refrence_modify_add_byRef(x, y) << endl; // refrence_modify_add_byRef:8
cout << "x=" << x << endl; // x=6

}

关于为什么通过值拷贝的方式捕获的外部变量是只读的:

lambda表达式的类型在C11中会被看做是一个带 operator() 的类,即仿函数。
按照C
标准,lambda表达式的 operator() 默认是 const 的,一个 const 成员函数是无法修改成员变量值的。
mutable 选项的作用就在于取消 operator ()const 属性。

因为 lambda 表达式在 C++ 中会被看做是一个仿函数,因此可以使用std::function和std::bind来存储和操作lambda表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void testLambdawithfunction()
{
// 包装可调用函数
std::function<int(int)> f1 = [](int a) {return a; };
// 绑定可调用函数
std::function<int(int)> f2 = bind([](int a) {return a; }, placeholders::_1);

// 绑定可修改的函数
std::function<int(int&, int)> f3 = [](int& a, int b) { a = 3; return a + b; };
// 绑定可修改的函数
std::function<int(int&, int)> f4 = bind([](int& a, int b) { a = 4; return a + b; }, std::placeholders::_1, std::placeholders::_2);


int x = 2, y = 2;

// 函数调用
cout << "f1(100)" << f1(100) << endl; // f1(100)100
cout << "f2(200)" << f2(200) << endl; // f2(200)200
cout << "f3(x, y)" << f3(x, y) << endl; // f3(x, y)5
cout << "x=" << x << endl; // x=3
cout << "f3(x, y)" << f4(x, y) << endl; // f3(x, y)6
cout << "x=" << x << endl; // x=4
}

对于没有捕获任何变量的 lambda 表达式,还可以转换成一个普通的函数指针:

1
2
3
4
5
6
7
8
9
using func_ptr = int(*)(int);
// 没有捕获任何外部变量的匿名函数
func_ptr f = [](int a)
{
return a;
};

// 函数调用
f(1314);

参考文章


【CPP】Lambda表达式
https://hodlyounger.github.io/B_Code/CPP/【CPP】Lambda表达式/
作者
mingming
发布于
2023年10月27日
许可协议