Lambda表达式随记
学习链接
目录
- 作用
- 定义
- [capture list] 捕获列表
- (paramter) 参数列表
- mutable 可变规格
- throw() 异常说明
- -> return-type 返回类型
- {function statement} lambda函数体
- Lambda表达式的优缺点
- Lambda表达式工作原理
- 适用场景
- STL算法库
- 短小不需要复用函数场景
作用
Lambda表达式:在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。
定义
[capture list] (paramter) mutable throw() -> return-type {function statement}
以上依次为 捕获列表、参数列表、可变规格、异常说明、返回类型、lambda函数体。下面依次进行说明与详解。
[capture list] 捕获列表
C++规范中也称为Lambda导入器。[ ]是Lambda的引出符,编译器是根据这个引出符来判断接下来的代码是否为Lambda表达式,捕获列表可以捕捉上下文中的变量以供Lambda函数使用。
捕获列表有以下这些常用形式:
- []表示不捕获任何变量
- [var]表示值传递方式捕获变量var
- [&var]表示引用传递捕获变量var
int num = 100;
auto function1 = ([num]{
std::cout << num << std::endl;
}
);//Lambda函数定义
function1();//函数调用
auto function2 = ([&num]{
std::cout << num << std::endl;
}
);
function2();
- [=]表示值传递方式捕获所有父作用域的变量(其中也包括this)
- [&]表示引用传递方式捕获所有父作用域的变量(其中也包括this)
int index = 1;
int num = 100;
auto function1 = ([=]{
std::cout << "index: "<< index << ", "
<< "num: "<< num << std::endl;
}
);
function1();
auto function2 = ([&]{
num = 1000;
index = 2;
std::cout << "index: "<< index << ", "
<< "num: "<< num << std::endl;
}
);
function2();
- [this]表示值传递方式捕捉当前的this指针
#include <iostream>
using namespace std;
class Lambda
{
public:
void sayHello() {
std::cout << "Hello" << std::endl;
};
void lambda() {
auto function = [this]{
this->sayHello();
};
function();
}
};
int main()
{
Lambda demo;
demo.lambda();
}
- [=, &] 拷贝与引用混合
- [=, &a, &b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量
- [&, a, this]表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其它所有变量
- 要注意的是,捕捉列表不允许变量的重复捕捉,如:[=,a]这里已经以值传递方式捕捉了所有变量,但是重复捕捉a了,会报错的;[&,&this]这里&已经以引用传递方式捕捉了所有变量,再捕捉this也是一种重复。
int index = 1;
int num = 100;
auto function = ([=, &index, &num]{
num = 1000;
index = 2;
std::cout << "index: "<< index << ", "
<< "num: "<< num << std::endl;
}
);
function();
(paramter) 参数列表
除了捕获列表之外,Lambda还可以接受输入参数。参数列表是可选的,并且在大多数方面类似于函数的参数列表。
int index = 1;
int num = 100;
auto function = ([=, &index, &num]{
num = 1000;
index = 2;
std::cout << "index: "<< index << ", "
<< "num: "<< num << std::endl;
}
);
function();
mutable 可变规格
mutable关键字可以打破const类型的限制,使得const类型的方法也可以修改成员变量的值。
默认情况下Lambda函数总是一个const函数(在这里就是不能修改参数列表变量,因为参数列表的变量相当于Lambda表达式工作原理里的类成员变量),mutable可以取消其常量性。
但是在使用该修饰符时,参数列表不可省略(即使参数为空)。
#include <iostream>
using namespace std;
int main()
{
int m = 0;
int n = 0;
[&, n] (int a) mutable { m = ++n + a; }(4);
cout << m << endl << n << endl;
}
throw() 异常说明
你可以使用 throw() 异常规范来指示 Lambda 表达式不会引发任何异常。与普通函数一样,如果 Lambda 表达式声明 C4297 异常规范且 Lambda 体引发异常,Visual C++ 编译器将生成警告 throw() 。
int main() // C4297 expected
{
[]() throw() { throw 5; }();
}
-> return-type 返回类型
声明lambda函数的返回类型。但是这部分一般直接省略,编译器通过lambda函数体可对返回类型进行推导,无需特意声明返回类型。
{function statement} lambda函数体
内容与普通函数一致,只不过除了像普通函数那样可以使用参数列表里的参数之外,还可以使用捕获列表中所捕获到的参数。且没有mutable可变规格修饰时,传入的参数均为const传入,无法修改。
Lambda表达式的优缺点
优点:可以直接在需要调用函数的位置定义短小精悍的函数,而不需要预先定义好函数;使用Lamdba表达式变得更加紧凑,结构层次更加明显、代码可读性更好;
缺点:Lamdba表达式语法比较灵活,增加了阅读代码的难度;对于函数复用无能为力。
Lambda表达式工作原理
编译器会把一个Lambda表达式生成一个匿名类的匿名对象,并在类中重载函数调用运算符,实现了一个operator()方法。
auto print = []{cout << "Hello World!" << endl; };
//相当于:
class print_class
{
public:
void operator()(void) const
{
cout << "Hello World!" << endl;
}
};
// 用构造的类创建对象,print此时就是一个函数对象
auto print = print_class();
//所以这也可以解释下面这段代码:
[&, n] (int a) mutable { m = ++n + a; }(4);
//这里的(4)其实就相当于调用了operator()重载函数而已
适用场景
STL算法库
//for_each应用实例:
int a[4] = {11, 2, 33, 4};
sort(a, a+4, [=](int x, int y) -> bool { return x%10 < y%10; } );
for_each(a, a+4, [=](int x) { cout << x << " ";} );
//find_if应用实例:
int x = 5;
int y = 10;
deque<int> coll = { 1, 3, 19, 5, 13, 7, 11, 2, 17 };
auto pos = find_if(coll.cbegin(), coll.cend(), [=](int i) {
return i > x && i < y;
});
//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.date.begin(), vec_data.end(), [](int i) {
return n < x;}), vec_data.end());
std::for_each(vec.date.begin(), vec_data.end(), [](int i) {
std::cout << i << std::endl;});
短小不需要复用函数场景
//sort函数:
vector<int> testdata;
testdata.insert(testdata.begin(), data, data + 6);
sort(testdata.begin(), testdata.end(), [](int a, int b){ return a > b; });
//Lamdba表达式应用于函数指针与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;
}
//Lamdba表达式作为函数的入参
using FuncCallback = std::function<void(void)>;
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);
//Lamdba表达式在QT中的应用
QTimer *timer=new QTimer;
timer->start(1000);
QObject::connect(timer, &QTimer::timeout, [&](){
qDebug() << "Lambda表达式";
});
QTimer::singleShot(2000,[](){
qDebug() << __FUNCTION__ << "QTimer::singleShot with Lambda表达式。";
});
int a = 10;
QString str1 = "汉字博大精深";
connect(pBtn4, &QPushButton::clicked, [=](bool checked){
qDebug() << a <<str1;
qDebug() << checked;
qDebug() << "Hua Windows Lambda Button";
});