C++之Lambda表达式
参考
Lambda是C++11引入的匿名函数对象,算是一个语法糖,而且还在不断发展。
1. 特点
- 就地定义,无需单独命名
- 可捕获上下文变量
- 可自动推导返回类型(多数情况)
- 可作为函数参数传递
2. 语法
[capture](parameters) mutable -> return_type { //Body }
-
[capture]:捕获列表,用于指定Lambda如何访问外部变量。
-
(parameters):参数列表,和普通函数的参数类似,但需要注意Lambda参数C++14起才支持auto类型。且不允许有默认参数。
-
mutable:这个关键字的作用是允许修改按值捕获的变量,或者调用非const的成员函数。默认情况下Lambda的operator()是const的,所以不加mutable的话,无法修改按值捕获的变量。
-
return_type:返回类型,通常可以自动推导,但在某些情况下需要显式指定,比如函数体内有多个return语句且返回类型不一致时。
-
函数体:Lambda的具体实现代码,和普通函数类似,但可以访问捕获的变量。
3. 示例
这是官方给出的一个标准示例,实例中std::sort函数第三个参数应该是传递一个排序规则的函数,但是这个实例中直接将排序函数的实现写在应该传递函数的位置,省去了定义排序函数的过程,对于这种不需要复用,且短小的函数,直接传递函数体可以增加代码的可读性。
#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));
} // end of lambda expression
);
}
4. Lambda表达式参数详解
4.1 Lambda捕获列表
Lambda表达式与普通喊数最大的区别在于,除了可以使用参数外,Lambda表达式还可以通过捕获列表访问一些上下文中的数据。具体地,捕获列表描述了上下文那些数据可以被Lambda使用,以及使用方式(值传递或引用传递)。语法上,在[]包括起来的的是捕获列表,捕获列表有多个捕获项组成,并以逗号分隔。捕获列表有以下几种形式:
[]表示捕获任何变量
auto function = ([]{
std::cout << hello world! << std::endl;
}
);
function();
[var]表示值传递方捕获变量var,外面只改变并不会改变Lambda捕获时的值
int main(int argc, char** argv) {
int num = 100;
auto function = ([num]{
std::cout << "num1: " << num << std::endl;
}
);
num = 1000;
function();
std::cout << "num2: " << num << std::endl;
}

[=]表示值传递方式捕获所有父作用域的变量,包括this
int main(int argc, char** argv) {
int num = 100;
auto function = ([=]{
std::cout << "num1: " << num << std::endl;
}
);
num = 1000;
function();
std::cout << "num2: " << num << std::endl;
}

[&var]表示引用传递方式捕获变量,跟值传递不同,外部值改变会实时更新,lambda表达式内也能更改捕获的值
int main(int argc, char** argv) {
int num = 100;
auto function = ([&num]{
std::cout << "num1: " << num << std::endl;
num = 11;
}
);
num = 1000;
function();
std::cout << "num2: " << num << std::endl;
}

[=,&]拷贝与引用混用
int main(int argc, char** argv) {
int index = 1;
int num = 100;
auto function = ([=,&num]{
num = 1000;
std::cout << "index1: " << index << "num1: " << num << std::endl;
}
);
function();
}

4.2 Lambda参数列表
int main(int argc, char** argv) {
int index = 1;
int num = 100;
auto function = [](int first, int second){
return first + second;
};
auto ret = function(100,200);
std::cout << "ret: " << ret << std::endl;
}

4.3 可变规格mutable
默认情况下,Lambda函数总是一个const函数,mutable修饰符可以取消其常亮性。在使用该修饰符时,参数列表不可省略(即使参数为空)。
#include <iostream>
using namespace std;
int main()
{
int m = 0;
int n = 0;
[&, n] (int a) mutable {
m = ++n + a; //不加mutable,提示++n报错
}(4);
cout << m << endl << n << endl;
}

4.4 返回类型
Lambda表达式的返回类型会自动推导。除非你指定了返回类型,否则不必使用关键字。返回型类似于通常的方法或函数的返回型部分。但是,返回类型必须在参数列表之后,并且必须在返回类型->之前包含类型关键字。如果lambda主体仅包含一个return语句或该表达式未返回值,则可以省略Lambda表达式的return-type部分。如果lambda主体包含一个return语句,则编译器将从return表达式的类型中推断出return类型。否则,编译器将返回类型推导为void。
auto x1 = [](int i){ return i; };
4.5 Lambda函数体
5. Lambda适用场景
- 应用于STL算法库
#include <algorithm>
#include <cmath>
using namespace std;
int main()
{
// for_each应用实例
int a[4] = {33,4,11,2};
sort(a, a+4, [=](int x, int y) -> bool {
return x%10 < y%10;
} );
for_each(a, a+4, [=](int x) {
cout << x << " ";
} );
}

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
int main()
{
// remove_if应用实例
std::vector<int> vec_data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int x = 5;
vec_data.erase(std::remove_if(vec_data.begin(), vec_data.end(), [=](int i) {
return i < x;}), vec_data.end());
std::for_each(vec_data.begin(), vec_data.end(), [](int i) {
std::cout << i << std::endl;});
}

- 短小不需要复用函数场景
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void)
{
int data[6] = { 3, 4, 12, 2, 1, 6 };
vector<int> testdata;
testdata.insert(testdata.begin(), data, data + 6);
// 对于比较大小的逻辑,使用lamdba不需要在重新定义一个函数
sort(testdata.begin(), testdata.end(), [](int a, int b){
return a > b; });
for (int a : testdata)
{
std::cout << a << std::endl;
}
return 0;
}

- 应用于多线程场景
clude <iostream>
#include <thread>
#include <vector>
#include <algorithm>
int main()
{
// vector 容器存储线程
std::vector<std::thread> workers;
for (int i = 0; i < 5; i++)
{
workers.push_back(std::thread([]()
{
std::cout << "thread function\n";
}));
}
std::cout << "main thread\n";
// 通过 for_each 循环每一个线程
// 第三个参数赋值一个task任务
// 符号'[]'会告诉编译器我们正在用一个匿名函数
// lambda函数将它的参数作为线程的引用t
// 然后一个一个的join
std::for_each(workers.begin(), workers.end(), [](std::thread &t)
{
t.join();
});
return 0;
}

- 应用于函数指针与function
#include <iostream>
#include <functional>
using namespace std;
int main(void)
{
int x = 8, y = 9;
auto add = [](int a, int b) { return a + b; };
std::function<int(int, int)> Add = [=](int a, int b) { return a + b; };
cout << "add: " << add(x, y) << endl;
cout << "Add: " << Add(x, y) << endl;
return 0;
}

- 作为函数的入参
void DataCallback(FuncCallback callback)
{
std::cout << "Start FuncCallback!" << std::endl;
callback();
std::cout << "End FuncCallback!" << std::endl;
}
auto callback_handler = [&](){
std::cout << "This is callback_handler";
};
DataCallback(callback_handler);
- 在QT中的应用
QTimer *timer=new QTimer;
timer->start(1000);
QObject::connect(timer,&QTimer::timeout,[&](){
qDebug() << "Lambda表达式";
});
int a = 10;
QString str1 = "汉字博大精深";
connect(pBtn4, &QPushButton::clicked, [=](bool checked){
qDebug() << a <<str1;
qDebug() << checked;
qDebug() << "Hua Windows Lambda Button";
});
浙公网安备 33010602011771号