C++之Lambda表达式详细解释

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表达式赋值给对应类型的函数指针时,捕获列表[]中一定要为空!

猜你喜欢

转载自blog.csdn.net/weixin_45590473/article/details/111396362
今日推荐