C++ Lambda
C++ Lambda 表达式允许我们定义匿名函数对象(functors 仿函数),这些对象既可以内联使用,也可以作为单个参数传递。
Lambda表达式是在C++11版本中被引入,以采用更方便、更简洁的方式创建匿名仿函数。
通过使用Lambda表达式,我们不再需要在一个单独的类或结构中重载()操作符。
用C++创建一个Lambda表达式
基本的Lambda表达式可以是这样:
auto hello = []() {
// lambda 函数体
};
其中,
[]
被称为 lambda引用器 表示lambda表达式的开始()
被称为 参数列表 与平常函数中的()操作符相似
上列代码等价于:
void hello() {
// 函数体
}
同一般函数一样,我们可以用下列方法调用lambda表达式:
hello();
注意: 使用了auto关键字来自动判断lambda表达式的返回类型
例1: C++ Lambda 函数
#include <iostream>
using namespace std;
int main() {
// 用Lambda函数打印 Hello world
auto hello = []() {
cout << "Hello World!";
};
// 调用lambda函数
hello();
return 0;
}
输出
Hello World!
在上面的例子中,我们创建了一个简单的程序,用C++ lambda表达式打印了“Hello World!”
I. 创建了lambda函数并将其分配给一个名为 hello 的变量
auto hello = []() {
cout << "Hello World!";
};
II. 用了hello作为变量名称,同时加上()操作符完成了对lambda函数的调用:
// 打印 "Hello World!"
hello();
带有参数的C++ Lambda函数
同正则函数一样,lambda表达式也可以接受参数。比如:
#include <iostream>
using namespace std;
int main() {
// 带有两个整数型的lambda函数
// 带参并打印他们的和
auto add = [] (int a, int b) {
cout << "Sum = " << a + b;
};
// 调用lambda表达式
add(100, 10);
return 0;
}
输出
Sum = 110
在上面的例子中,我们创建了一个lambda函数,能够接收两个整数参数并显示它们的总和。
auto add = [] (int a, int b) {
cout << "Sum = " << a + b;
};
等价于:
void add(int a, int b) {
cout << "Sum = " << a + b;
}
然后我们通过传递两个整数参数来调用lambda函数:
// 返回 110
add(100, 10);
带有返回类型的C++ Lambda函数
与普通函数一样,C++ lambda表达式也可以有一个返回类型。
编译器可以根据返回语句隐式地推断出lambda表达式的返回类型。
auto add = [] (int a, int b) {
// 返回 int 类型数据
return a + b;
};
在上述例子中,我们没有明确定义lambda函数的返回类型。这是因为return语句是有一个,编译器能够判断清楚。
但是对于不同数据类型的多个返回语句,我们必须明确地定义其类型。比如说:
auto operation = [] (int a, int b, string op) -> double {
if (op == "sum") {
// 返回int类型
return a + b;
}
else {
// 返回double类型
return (a + b) / 2.0;
}
};
注意上面的代码 -> double。明确地定义了返回类型为double,因为有多个语句根据op的值返回不同的类型数值。
不管各种返回语句返回的是什么类型的值,它们都将被显式转换为double类型返回。
例 2: C++ Lambda - 显式返回类型
#include<iostream>
using namespace std;
int main() {
// lambda 函数显示返回类型 'double'
// 根据操作符返回sum还是average
auto operation = [] (int a, int b, string op) -> double {
if (op == "sum") {
return a + b;
}
else {
return (a + b) / 2.0;
}
};
int num1 = 1;
int num2 = 2;
// 使用sum来标记完成对num1,num2的计算
auto sum = operation(num1, num2, "sum");
cout << "Sum = " << sum << endl;
// 使用average来标记完成对num1,num2的计算
auto avg = operation(num1, num2, "avg");
cout << "Average = " << avg;
return 0;
}
输出
Sum = 3
Average = 1.5
在上面的例子中,我们创建了一个lambda函数来寻找操作数:
- 两个整数和 sum
- 两个整数的平均 avg
auto operation = [] (int a, int b, string op) -> double {
if (op == "sum") {
// 返回 'int'
return a + b;
}
else {
// 返回 'double'
return (a + b) / 2.0;
}
};
在main()中,我们需要首先通过传递 "sum "作为第三个参数,从而得出num1和num2的总和:
auto sum = operation(num1, num2, "sum");
在这里,尽管lambda返回一个整数值,但它被显式转换为double类型。
然后,我们通过传递一些其他的字符串作为参数来得出两数的平均值:
auto avg = operation(num1, num2, "avg");
C++的Lambda函数捕获子句
默认情况下,lambda函数不能访问嵌套函数中的变量。如果需要访问这些变量,我们则要使用捕获子句。
可以通过以下两种方式捕获变量:
按值捕获
这类似于按值调用。在这里,lambda函数创建时会对实际值做一次拷贝。
注意: 我们只能读取lambda函数体中的变量,但不能修改它。
通过将值捕获过来的lambda表达式:
int num_main = 100;
// 从函数体中获取对num_main的访问
auto my_lambda = [num_main] () {
cout << num_main;
};
这里,[num_main]允许lambda访问num_main变量。
如果我们把num_main从捕获子句中删除,我们将会犯一个错误,因为num_main不能从lambda函数体中访问。
通过引用捕获
这类似通过引用的方式来捕获,即lambda函数可以访问变量地址。
注意: 我们可以读取变量,也可以在lambda函数中修改它。
一个带有引用捕获的基本lambda表达式如下:
int num_main = 100;
// 访问num_main变量的地址
auto my_lambda = [&num_main] () {
num_main = 900;
};
在[&num_main]中我们使用了&操作符。这表明我们正在捕捉num_main变量的地址。
例3:C++ Lambda按值捕获
#include<iostream>
using namespace std;
int main() {
int initial_sum = 100;
// 按initial_sum的值进行捕获
auto add_to_sum = [initial_sum] (int num) {
return initial_sum + num;
};
int final_sum = add_to_sum(78);
cout << "100 + 78 = " << final_sum;
return 0;
}
输出
100 + 78 = 178
在上面的例子中,我们创建了一个lambda表达式,返回一个名为initial_sum的局部变量和一个整数参数num的总和。
auto add_to_sum = [initial_sum] (int num) {
return initial_sum + num;
};
这里,[initial_sum]通过捕获initial_sum的值传入到函数内。
然后,我们调用该函数并将其返回值存储在final_sum变量中。
int final_sum = add_to_sum(78);
在这里lambda函数中:
- num 是 78
- initial_sum 是 100
因此结果变为 100 + 78 也就是 178.
注意:假设我们想通过数值来捕获多个变量。比如:
auto my_lambda = [a, b, c, d, e] (){ // lambda 函数 }
正如所见,如果将变量一个一个列出将使整个过程变得十分繁琐,为了使操作更加简单,我们可以简单地尝试用隐式按值捕获,例如:
auto my_lambda = [=] (){ // lambda 函数 }
这里,[=]表示函数中的所有变量都被值所捕获。
例4:C++ Lambda通过引用捕获
#include <iostream>
using namespace std;
int main() {
int num = 0;
cout << "Initially, num = " << num << endl;
// [&num] 捕获 num 变量的地址
auto increment_by_one = [&num] () {
cout << "Incrementing num by 1.\n";
num++;
};
// invoke lambda function
increment_by_one();
cout << "Now, num = " << num << endl;
return 0;
}
输出
Initially, num = 0
Incrementing num by 1.
Now, num = 1
在上面的例子中,我们创建了一个lambda函数,将一个局部变量num的值增加了1。
auto increment_by_one = [&num] () {
cout << "Incrementing num by 1.\n";
num++;
};
这里[&num]被用来通过引用来捕获num。
一开始,num的值是0。
然后,我们调用lambda表达式increment_by_one()。使num的值增加了1。
注意: 为了捕获嵌套函数中的所有变量, 我们可以通过使用隐式的引用捕获。例如:
auto my_lambda = [&] (){
// lambda 函数
}
这里,[&]表示所有的变量都通过引用来捕获。
例子: C++ Lambda函数作为STL算法的参数
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
// 初始化整数类型vector
vector<int> nums = {1, 2, 3, 4, 5, 8, 10, 12};
int even_count = count_if(nums.begin(), nums.end(), [](int num) {
return num % 2 == 0;
});
cout << "There are " << even_count << " even numbers.";
return 0;
}
输出
There are 5 even numbers.
在上面的例子中,我们在count_if算法中使用了一个lambda函数来计算nums向量中的全部偶数:
int even_count = count_if(nums.begin(), nums.end(), [](int num) {
return num % 2 == 0;
});
请注意,我们用lambda表达式作为count_if的第三个参数。这个lambda表达式接收整数num。如果num是偶数,则返回true。
另外,我们还把lambda表达式写在了参数列表中。
[](int num) {
return num % 2 == 0;
}