[C++] C++11详解 (四)lambda表达式

水墨不写bug 2024-09-04 09:05:02 阅读 51

标题:[C++] C++11详解 (四)lambda表达式

@水墨不写bug



目录

一、lambda表达式

 lambda表达式语法

lambda表达式与仿函数关系


正文开始:

一、lambda表达式

        作为C++学习者,你一定对algorithm中的sort函数十分熟悉,sort函数默认可以对自定义类型的数据按照升序排序。在实际生活中,我们常常遇到的场景是需要对自定义类型对象排序。

        如何对自定义类型排序?其实就是按照某一个规则,比如是这种类型的某一成员变量的大小来决定大小顺序。如果想要实现这样的功能,我们首先想到的是对比较运算符“==,>,<”重载。

<code>#include<iostream>

using namespace std;

class STUDENT

{

public:

STUDENT(const string& name = "",const string& id = "",double score = 0)

: _name(name)

, _id(id)

,_score(score)

{}

bool operator>(const STUDENT& s)

{

return _score > s._score;

}

bool operator<(const STUDENT& s)

{

return _score < s._score;

}

bool operator==(const STUDENT& s)

{

return _score == s._score;

}

private:

string _name;

string _id;

double _score;

};

int main()

{

STUDENT s1("zhangsan","23134",90);

STUDENT s2("lisi", "23135", 89);

if (s1 > s2)

cout << "s1>s2";

return 0;

}

s1>s2

        但是这样写的不足是代码写死了,只能按照score比较,如果想要按照id排序,就还要修改源码,十分不方便。

        其次,你可能还会想到这样写,用函数对象,仿函数来实现比较:

#include<iostream>

#include<algorithm>

#include<vector>

using namespace std;

struct STUDENT

{

public:

STUDENT(const string& name = "",const string& id = "",double score = 0)

: _name(name)

, _id(id)

,_score(score)

{}

string _name;

string _id;

double _score;

};

struct scoreLESS

{

bool operator()(const STUDENT& s1,const STUDENT& s2)

{

return s1._score < s2._score;

}

};

struct scoreGREATER

{

bool operator()(const STUDENT& s1, const STUDENT& s2)

{

return s1._score > s2._score;

}

};

int main()

{

vector<STUDENT> vs = { {"zhangsan","123",80},{"lisi","231",90},{"wangwu","213",100}};

sort(vs.begin(), vs.end(),scoreGREATER());

for (int i = 0; i < vs.size(); ++i)

{

cout << vs[i]._name << " " << vs[i]._id << " 分数" << vs[i]._score<<endl;

}

return 0;

}

wangwu 213 分数100

lisi 231 分数90

zhangsan 123 分数80

        但是,上面仿函数需要写很多,并且命名多又杂,为了实现一个algorithm算法,都要重新去写一个类,如果每次比较的逻辑不一样,还要去实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式。

 

        如果我们使用lambda表达式来实现上述的对分数的排序,如下所示:

#include<iostream>

#include<algorithm>

#include<vector>

using namespace std;

struct STUDENT

{

public:

STUDENT(const string& name = "",const string& id = "",double score = 0)

: _name(name)

, _id(id)

,_score(score)

{}

string _name;

string _id;

double _score;

};

void Print(const vector<STUDENT>& vs)

{

for (int i = 0; i < vs.size(); ++i)

{

cout << vs[i]._name << " " << vs[i]._id << " 分数" << vs[i]._score << endl;

}

cout << "___________________________"<<endl;

}

int main()

{

vector<STUDENT> vs = { {"zhangsan","123",98},{"lisi","231",90},{"wangwu","913",100}};

//分数升序

sort(vs.begin(), vs.end(),[](const STUDENT& s1,const STUDENT& s2) ->bool{

return s1._score < s2._score;

});

Print(vs);

//分数降序

sort(vs.begin(), vs.end(), [](const STUDENT& s1, const STUDENT& s2) ->bool {

return s1._score > s2._score;

});

Print(vs);

//学号升序

sort(vs.begin(), vs.end(), [](const STUDENT& s1, const STUDENT& s2) ->bool {

return s1._id < s2._id;

});

Print(vs);

//学号降序

sort(vs.begin(), vs.end(), [](const STUDENT& s1, const STUDENT& s2) ->bool {

return s1._id > s2._id;

});

Print(vs);

return 0;

}

lisi 231 分数90

zhangsan 123 分数98

wangwu 913 分数100

___________________________

wangwu 913 分数100

zhangsan 123 分数98

lisi 231 分数90

___________________________

zhangsan 123 分数98

lisi 231 分数90

wangwu 913 分数100

___________________________

wangwu 913 分数100

lisi 231 分数90

zhangsan 123 分数98

___________________________


 lambda表达式语法

        lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement}

lambda表达式各部分说明:

        [capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda函数使用。

        (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。

        mutable:(捕捉的变量是否可变)默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

        ->return type:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。

        {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

注意:

        在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数(lambda表达式实际是一个匿名函数)为:[]{}; 该lambda函数不能做任何事情。


接下来,我们分别逐个讲解:

        1.捕获列表:

    捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用:

                [var]:表示值传递方式捕捉变量var;

                [=]:表示值传递方式捕获父作用域中所有的变量(包括this);

                [&var]:表示引用传递捕捉变量var;

                [&]:表示引用传递捕捉父作用域中的所有变量(包括this);

                [this]:表示值传递方式捕捉当前的this指针;

        注意:

        i,父作用域是指包含这个lambda的语句块;

        ii,语法上,捕捉列表可以有多个捕捉项,捕捉项之间用“,”分割。

             比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量

                        [&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量

        iii,捕捉列表不允许变量重复传递,否则就会导致编译错误。

        比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复(需要与ii区分,ii中的捕捉项之间没有交集,不是重复传递)

        iv,在块作用域以外的lambda函数捕捉列表必须为空。

        v,在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错。

        vi,lambda表达式之间不能相互赋值,即使看起来类型相同。

 


        2.参数列表:

        lambda表达式类似于一个函数,这里的参数列表就类似于函数的参数列表,在调用的时候传递的参数的类型,个数等。

        3.mutable:

        其实mutable具有误导性,lambda是一个匿名函数,如果传值捕捉,lambda内的参数实际上是一份拷贝,类似于传值调用,就类似于函数的形参变化不会影响实参:

#include<iostream>

using namespace std;

int main()

{

int a = 1, b = 2;

cout << a << " " << b << endl;

auto swap = [a, b]()mutable {

int tem = a;

a = b;

b = tem;

};

swap();

cout << a << " " << b << endl;

return 0;

}

1 2

1 2

如果想要改变实参,就需要传引用,而穿引用就不需要加mutable了:

#include<iostream>

using namespace std;

int main()

{

int a = 1, b = 2;

cout << a << " " << b << endl;

auto swap = [&a, &b]() {

int tem = a;

a = b;

b = tem;

};

swap();

cout << a << " " << b << endl;

return 0;

}

1 2

2 1

        换个角度想想,lambda设置为const函数,是有道理的,为的是防止我们误解:传值的时候把形参和实参错误联系起来。

        4.返回值类型:

        若为void,可以省略;若可以一眼看出,也可以不写,但是一般除了void,都要手动加上返回值类型。

        5.函数体:

        与函数相同。

        lambda表达式实际上可以理解为无名函数,该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个变量。

 

lambda表达式与仿函数关系

        函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator()运算符的类对象

        从使用方式上来看,函数对象与lambda表达式完全一样;

        实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载operator()。


完~

未经作者同意禁止转载 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。