1. Lambda表达式的简述
Lambda表达式是C++11引入的一种函数对象,其使用更为简洁,能更方便的嵌入需要函数的地方。
Lambda表达式也被称为匿名函数,其不需要特定的函数名称,甚至不用指定参数和返回类型。
合理使用Lambda表达式能有效提高代码的可读性,减少代码的冗余量,使代码更为简洁、高效、安全。
2. Lambda表达式的语法格式
Lambda表达式可分为五个部分,依次为:捕捉列表、参数列表、关键字、返回类型、函数体。
整体格式即为:
[捕获列表](参数列表) 关键字 -> 返回类型 {
函数体
};
其中,参数列表、关键字、返回类型均可以省略,即最简格式可以为:
[捕获列表] {
函数体
};
- 省略
[参数列表]
表明是一个无参函数; - 省略
关键字
表明捕捉参数列表中的常量属性,一般都会省略; - 省略
-> 返回类型
表明不手动指定返回类型,编译器可以自动推导默认返回类型。
2.1 []
捕获列表
不可省略
“捕获”决定了函数内能否访问或使用外部变量,如果允许捕获,那么在此函数体也可以使用同类环境中的全局和局部变量。
通过方括号内的符号指定当前上下文中的变量如何被捕获,可以选择以值的方式捕获或者以引用的方式捕获,也可以选择捕获全部变量或者指定变量。
[ ]
:不允许捕获外部变量。[=]
:表示以值捕获所有外部变量。[&]
:表示以引用捕获所有外部变量。[x]
:表示以值捕获指定的变量x。[&x]
:表示以引用捕获指定的变量x。
注意:指定捕获变量时,可以通过逗号分隔,实现混合捕获,也可以捕获多个指定变量,如[=, &x, &y]
表示以值方式捕获其他变量,以引用方式捕获x和y
示例:
/// 捕获列表为空
[] { cout << "hello world"; };
/// 以值的方式捕获,只读外部变量
string a = "hello world";
[=] { cout << a; };
/// 分别以值的方式和引用的方式捕获多个变量,
/// 以引用方式捕获变量可读写
string a = "";
string b = "world"
[&a, b] {
a = "hello";
cout << a << b;
};
2.2 ()
参数列表
可连同()括号一起省略
与普通函数的参数列表一样,定义Lambda表达式中所使用的输入参数。
示例:
/// 无参函数的方式见前面示例
/// 以值的方式捕获,只读外部变量
[](int a) { cout << a; };
2.3 关键字
只有mutable,可省略,一般都省略
使用mutable
关键字删除捕获变量的常量特性,允许函数体内修改捕获的变量。
示例:
int x = 1;
[x]() mutable {
x += 2; // 这样修改就是允许的
};
其意义不大,删除常量特性只对此Lambda表达式起作用,对外部没有意义,而其修改的功能完全可以被引用捕获代替,所以一般不会使用它。
2.4 ->
返回类型
可连同->符号一起省略
如果Lambda表达式有返回值,可以通过->
符号显式声明返回类型。
如果返回类型不需要指定,则可以省略,让其自动推导。
示例:
auto lambda1 = [](int x) { return x * x; }; // 自动返回int类型
auto lambda2 = [](int x) -> double { return x * x; }; // 显示指定返回double类型
int x = 1;
cout << lambda1(x) << lambda2(x);
2.5 {}
函数体
不可省略
同普通函数,在{}类实现函数逻辑和操作。
要注意的是,Lambda表达式也是一个语句,要在}后面加上封号;。
3. Lambda表达式的使用
3.1 作临时函数
Lambda表达式可用于一切需要值的情况,Lambda表达式可以对需要操作的变量进行初操作,然后利用返回值来满足这种需要值的情况。
另外,虽然说Lambda表达式是匿名函数,也确实将其作为匿名函数嵌入使用的情况更多,但其也可以赋给函数对象,与普通函数一样使用。
示例:
/// 声明lambda1变量为一个lambda表达式,可以同普通函数一样使用
auto lambda1 = [](int x) { return x * x; };
int x = 1;
int y = lambda1(x); /// 同普通函数一样使用
3.2 作参数传递
用于需要函数对象的参数,在很多场合都需要函数对象作为传入的参数,比如一些算法函数的参数等。
示例:
std::vector<int> vec = {5, 3, 8, 1};
/// 以Lambda表达式的方式传入自定义的比较函数
std::sort(vec.begin(), vec.end(), [](int a, int b) {
return a > b;
});
/// 使用Lambda表达式作为遍历操作的函数对象11111
std::for_each(vec.begin(), vec.end(), [](int x) {
std::cout << x * 2 << " ";
});
3.3 作回调函数
在事件驱动编程中,回调函数常常用于处理某些异步操作。使用Lambda表达式可以让回调函数的定义更加灵活,避免定义大量的单独函数。
示例:
/// 异步操作函数,callback为其回调函数
void async(std::function<void(int)> callback) {
int result = 42;
callback(result);
}
/// 使用Lambda表达式作为回调函数传递给异步操作函数
async([](int result) {
std::cout << "Async result: " << result << std::endl;
});
3.4 用于QT中信号和槽
在QT中,信号和槽是很常用的通信机制。通过Lambda表达式可以简化信号槽的连接和回调逻辑。
也可以通过使用Lambda表达式将格式完全不同的信号和函数连接起来。
示例:
void test(int a, int b, int c){....}
QPushButton button("Click Me");
QObject::connect(&button, &QPushButton::clicked, [&]() {
std::cout << "Button clicked!" << std::endl;
int a = 0;
int b = 1;
int c = 2;
test(a, b, c); /// 将格式完全不同的test函数与QPushButton中信号连接了起来
});