Lambda表达式
Lambda表达式格式
C++ 11 中的 Lambda 表达式用于定义并创建匿名的函数对象,以简化编程工作。
Lambda 的语法形式如下:
[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}
代码格式:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
using fptr = int(*)(int& obj);
int main()
{
fptr fobj = [](int& obj)mutable throw(int)->int {return obj; };
}
可以看到,Lambda 主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable 或 exception 声明、-> 返回值类型、{函数体}。
捕获列表:[捕获参数列表]
标识一个 Lambda 表达式的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义 Lambda 为止时 Lambda 所在作用范围内可见的局部变量(包括 Lambda 所在类的this)。函数对象参数有以下形式:
① 空:不捕获任何外部参数,捕获其实就是“把作用域内的变量拿来在函数体中直接用的意思”;
② =:函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量);
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
int a = 10;
vector<int> obj{ 1,2,3,4,5 };
for_each(obj.begin(), obj.end(), [=](int& obj)mutable {obj += a; cout << obj << endl; });
}
用值传递的方式传递一个变量:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
int a = 10;
vector<int> obj{ 1,2,3,4,5 };
for_each(obj.begin(), obj.end(), [a](int& obj)mutable {obj += a; cout << obj << endl; });
}
注:
Mutable的作用是可以在lambda函数体内部修改捕获列表捕获的变量值,但是由于我这里是按照值传递的方式传入参数,因此在lambda函数体内部改变a并不会引起外部a的变化。
③ &:函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是引用传递方式(相当于是编译器自动为我们按引用传递了所有局部变量);
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
int a = 10;
vector<int> obj{ 1,2,3,4,5 };
for_each(obj.begin(), obj.end(), [&](int& obj)mutable {obj += a; cout << obj << endl; });
}
用引用的方式捕获单个变量:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
int a = 10;
vector<int> obj{ 1,2,3,4,5 };
for_each(obj.begin(), obj.end(), [&a](int& obj)mutable {obj += a; cout << obj << endl; });
}
④ this:函数体内可以使用 Lambda 所在类中的成员变量;
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
#include <string>
#include <functional>
class Person
{
public:
int age;
string name;
public:
Person(int age, string name)
{
this->age = age;
this->name = name;
}
void ShowInf()
{
function<void()> fptr = [this]() {cout << this->name << "的年龄为" << this->age << endl; };
fptr(); // 调用lambda函数
}
};
int main()
{
Person obj(12, "张三");
obj.ShowInf();
}
注意:
⑴ 这里的lambda表达式的原型是function<返回数据类型(参数数据类型)>,和C语言中的函数指针有异曲同工之妙,其实两者本质上差不多,只不过lambda表达式多了个“捕获列表”而已;
⑵ 切记:由于我们传入的是this指针,因此类中不可以调用静态成员!
如下代码是否正确:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
#include <string>
#include <functional>
class Person
{
public:
int age;
string name;
static int mark;
public:
Person(int age, string name)
{
this->age = age;
this->name = name;
}
function<void()> ShowInf()
{
return [this]() {cout << this->name << "的年龄为" << this->age << endl; }; // 返回lambda表达式
}
};
int main()
{
Person obj(12, "张三");
obj.ShowInf();
}
这样是错误的。下面让我们探究一下为什么错误?
上面这段代码中的lambda表达式返回的数据类型是void,因此return lambda表达式的结果也是void类型。
Lambda函数新用法:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
#include <string>
#include <functional>
class Person
{
public:
int age;
string name;
static int mark;
public:
Person(int age, string name)
{
this->age = age;
this->name = name;
}
void ShowInf()
{
function<void()> fptr = [this]() {cout << this->name << "的年龄为" << this->age << endl; };
fptr(); // 调用lambda函数
}
};
int main()
{
Person obj(12, "张三");
obj.ShowInf();
int fptr1 = [&](int obj) {cout << obj << endl; return 0; }(4); // fptr1的数据类型是int整型
cout << typeid(fptr1).name() << endl;
}
大家观察一下上面的这段代码,其中如下代码最为显著:
int fptr1 = [&](int obj) {cout << obj << endl; return 0; }(4); // fptr1的数据类型是int整型
fptr是int类型,这是为什么呢?
function<int(int)> fptr1 = [&](int obj) {cout << obj << endl; return 0; };
fptr1(4);
等价代码如上所示,其实lambda表达式最后一个小括号就是用来传入参数的。
但是这样做有个前提,lambda表达式返回的数据类型不可以为void,对于void数据类型而言,这种形式是不可用的,即没有变量接收void类型的数据。
返回值数据类型为void的lambda表达式形式如下:
[&](int obj) {cout << obj << endl; }(9);
⑤ a,&b:将 a 按值传递,b 按引用进行传递;
⑥ =,&a,&b。除 a 和 b 按引用进行传递外,其他参数都按值进行传递;
⑦ &,a,b。除 a 和 b 按值进行传递外,其他参数都按引用进行传递。
捕获列表中可以定义无需声明数据类型的变量,并在lambda函数体内使用:
参数列表:(函数参数列表)
标识重载的 () 操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如: (a, b))和按引用 (如: (&a, &b)) 两种方式进行传递。
mutable 或 exception 声明
这部分可以省略。按值传递函数对象参数时,加上 mutable 修饰符后,可以修改传递进来的拷贝(注意是能修改拷贝,而不是值本身)。exception 声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw(int)。
-> 返回值类型
标识函数返回值的类型,当返回值为 void,或者函数体中只有一处 return 的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
{函数体}
标识函数的实现,这部分不能省略,但函数体可以为空。
将匿名函数实例化
把匿名函数存储在变量、数组或 vector 中,并把它们当做命名参数来传递:
#include <iostream>
using namespace std;
#include <functional>
#include <vector>
int main()
{
function<void(int, int)> fptr = [](int obj1, int obj2) {cout << obj1 + obj2 << endl; }; // 必须参数类型一一对应
fptr(1, 1);
vector<function<void(int, int)>> obj{ fptr };
obj[0](1, 2);
function<void(int, int)> fptr_array[1]{ fptr };
fptr_array[0](4, 6);
}
Lambda匿名函数与函数指针相互转化
#include <iostream>
using namespace std;
#include <functional>
using f_ptr = void(*)(int, int);
int main()
{
function<void(int, int)> fptr = [](int obj1, int obj2) {cout << obj1 + obj2 << endl; };
f_ptr fptr1 = [](int obj1, int obj2) {cout << obj1 + obj2 << endl; };
//f_ptr = fptr; // 不存在function<void(int,int)>对象转化至void(*)(int, int)对象
fptr1(1, 3);
}
注意:由于lambda表达式比函数指针更高级,即多了一个“可以捕获外部数据” 的功能,因此,我们在将lambda表达式赋值给对应类型的函数指针时,捕获列表[]中一定要为空!