当前位置: 首页 > article >正文

C++ 中的 Lambda 表达式

一个Lambda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数,可以在需要函数对象的地方直接定义和使用;与任何函数相似,一个lambda表达式具有一个返回类型,一个参数列表,一个函数体,但与函数不同,lambda表达式可以定义在函数内部。

Lambda表达式: 1.可以就地匿名定义目标函数或函数对象,不需要额外写一个函数 2.lambda是一个匿名的内联函数

lambda表达式本质是一个匿名函数,可以用函数指针来接收

作用是:提供一种简洁的方式来定义小型的,局部的函数,特别是在使用算法和函数式编程风格时十分方便。

Lambda表达式的语法:

[capture list捕获列表](parameter list参数列表) ->return 返回值类型{函数体}

捕获列表用于捕获外部变量,参数列表和返回类型与普通函数类似,函数体包含具体的操作。我们可以忽略参数列表和返回类型,但永远包含捕获列表和函数体,其中捕获列表内容通常为空。

Lambda表达式可以捕获局部变量,成员变量,全局变量

捕获方式有值捕获(按值复制外部变量),引用捕获(通过引用捕获外部变量)和混合捕获(同时使用值捕获和引用捕获)

捕获列表[]:捕获一定范围内的变量

参数列表(): 和普通函数的参数列表一样,如果没有参数参数列表可以省略不写

捕获列表

[ ] 不捕获任何变量

[&] 捕获外部作用域中的所有变量,并且按照引用捕获,引用捕获可读可写(修改)

[=]捕获外部作用域的所有变量,按照值捕获,拷贝过来的副本在函数体内是只读的

[= ,&a] 按值捕获外部作用域中的所有变量,并且按照引用捕获外部变量 a

[bar] 按值捕获bar变量,不捕获其他变量

[this] 捕获当前类中的this指针(this指针在类内成员函数里,静态成员函数不含this指针),让lambda表达式拥有和当前类成员函数同样的访问权限

一般情况下,不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值类型,但是需要注意的是lambda表达式不能通过列表初始化自动推导出返回值类型。

void fun()
{
        //lambda可以用函数指针来接收
        int(*p)() = []()->int { cout << "hello!" << endl; return 0; };//匿名函数 lambda
        p();//p是一个函数指针,指向函数的首地址,加()可以访问该元素
        //lambda可以用auto 变量名来接收
        auto f = []()-> int { cout<<"abc"<<endl; return 0; };
        f();
        //当没有函数参数时,()可以省略,当返回值类型是int时,int也可以省略,函数会自动识别返回值类型
        int(*w)() = [] { cout << "得分" << endl; return 0; };
        w();
}
int main()
{
        fun();
        return 0;
}

//不能在函数内部访问另一个函数的局部变量,但可以通过lambda的捕获列表来实现
//捕获列表的作用是帮助lambda捕获除全局变量以外,其他外部变量的
void work()
{   
        //int b;
        //auto a = [] {b; return 0; };//b报错 lambda的本质是函数,不能在函数内部访问另一个函数的局部变量
    
        //通过lambda的捕获列表来实现对另一个函数局部变量的访问
        int c,e;
        auto t = [c,&e] 
        {
            //c=3;//报错:c是值捕获,只可读不可以修改
            e = 8;//不报错: e是引用捕获,可读可修改
            cout << e << endl;
            return 0;
        };
        t();
}
int main()
{    
        work();
        return 0;
}

捕获列表的相关判断:

int main()
{
    int a = 10, b = 20;
    auto f1 = [] {return a; }; //错误,没有捕获外部变量,因此无法访问变量 a
    auto f2 = [&] {return a++; }; //正确,使用引用的方式捕获外部变量,可读写
    auto f3 = [=] {return a; }; //正确,使用值拷贝的方式捕获外部变量,可读
    auto f4 = [=] {return a++; };//错误,使用值拷贝的方式捕获外部变量,可读不能写
    auto f5 = [a] {return a + b; }//错误,使用拷贝的方式捕获了外部变量 a,没有捕获外部变量 b,因此无法访问变量 b
    auto f6 = [a, &b] {return a + (b++); };//正确,使用值拷贝的方式捕获了外部变量 a,只读,使用引用的方式捕获外部变量 b,可读写
    auto f7 = [=, &b] {return a + (b++); };//正确,使用值拷贝的方式捕获所有外部变量以及 b 的引用,b 可读写,其他只读
    return 0;
}
//this指针:this指针在类内成员函数的隐含参数里,当对象调用成员函数时,会将对象的地址传给this指针
//类内的成员函数只有一份代码放在代码区,所有对象调用的是同一个成员函数
//如果在成员函数内修改对象的成员变量是通过this指针来区分修改的是哪个对象的成员变量
//在类内,如果lambda在成员函数内想捕获成员变量,只需捕获this即可
class Test
{
public:
    void output(int x, int y)
    {
        auto x1 = [] {return m_number; }; //错误,没有捕获外部变量,不能使用类成员 m_number
        auto x1 = [ m_number] { return m_number; };//错误,不知道this指向谁,捕获的m_number是哪个对象的
        auto x2 = [=] {return m_number + x + y; };//正确,以值拷贝的方式捕获所有外部变量包括this,因为this是output的隐含参数
        auto x3 = [&] {return m_number + x + y; };//正确,以引用的方式捕获所有外部变量
        //通过捕获this指针,用this-> m_number去访问m_number
        auto x4 = [this] {return m_number; };//正确,捕获 this 指针,可访问对象内部成员
        auto x5 = [this] {return m_number + x + y; };//错误,捕获 this 指针,可访问类内部成员,没有捕获到变量 x,y,因此不能访问。
        auto x6 = [this, x, y] {return m_number + x + y; };// 正确,捕获 this 指针,x,y
        auto x7 = [this] {return m_number++; }; //正确,捕获 this 指针,并且可以修改对象内部变量的值
    }
    int m_number = 100;
};

返回值

一般情况下,不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值类型,但是需要注意的是lambda表达式不能通过列表初始化自动推导出返回值类型,因为不知道列表是哪种类型(有vector,list等等),需要正确显示声明了函数的返回值类型是哪种类型的。

// 可以自动推导出返回值类型
auto f = [](int i)
{
    return i;
}
// 不能推导出返回值类型
auto f1 = []()
{
    return { 1, 2 }; // 基于列表初始化推导返回值,错误
}
// 正确显示声明了函数的返回值类型是数组类型的
auto f1 = []()-> vector<int>
{
    return { 1, 2 }; // 正确
};
vector<int> a = {1,5,6};

函数指针

//函数比较器
//返回值为bool类型
#include <algorithm>
bool cmp(int a, int b)
{
        //return a > b;//从大到小排序
        return a < b;//从小到大排序
}
int main()
{
        vector<int> c = {0,-1,4,-2,-9,4,2,8};
        sort(c.begin(),c.end(),cmp);
        for (auto it : c)
        {
            cout << it << " ";
        }
        return 0;
}

class A {
public:
        string Name;
        int Age;
        A(string name, int age) :Name(name), Age(age) {}
};
bool cmp(const A &a,const A &b)//使用引用避免拷贝构造,const万能引用,既能接收左值又能接收右值
{
        return a.Age < b.Age;
}
int main()
{
        vector<A> c = {A("张三",12),A("李四",7),A("王五",19)};
        //sort(c.begin(), c.end(), cmp);
        sort(c.begin(), c.end(), [](const A& a, const A& b)
        {
                return a.Age < b.Age; //Age从小到大排序
        });
        for (auto it: c)
        {
            cout << it.Name << " " << it.Age << endl;
        }
        return 0;
}


http://www.kler.cn/a/410768.html

相关文章:

  • 【JavaEE初阶】多线程初阶下部
  • 亚信安全与飞书达成深度合作
  • React(五)——useContecxt/Reducer/useCallback/useRef/React.memo/useMemo
  • React表单联动
  • 利用开源图床的技巧与实践
  • 使用java模拟记录软件免费试用次数
  • 网络安全基础——网络安全法
  • Kubernetes 分布式存储后端:指南
  • 2024小迪安全基础入门第三课
  • 天洑参展第二届江苏省流体力学学术研讨和产学研会议
  • 第7章 HttpMessageConverter
  • 智尚招聘求职小程序V1.0.18
  • 附录2-pytorch yolov5目标检测
  • 钉钉授权登录
  • C语言使用指针的15大陷阱
  • odoo17 档案管理之翻译
  • Android 应用测试的各种环境问题记录(Instrumentation测试)
  • c#:winform 获取本地打印机列表(下拉列表实现)
  • HCIA笔记3--TCP-UDP-交换机工作原理
  • 设计模式之策略模式-工作实战总结与实现
  • CPU性能优化--skylake 微架构事件编码示例
  • Android蓝牙架构,源文件目录/编译方式学习
  • idea怎么打开两个窗口,运行两个项目
  • 【LeetCode: 743. 网络延迟时间 + Dijkstra】
  • Vercel 设置自动部署 GitHub 项目
  • .net 支持跨平台(桌面)系列技术汇总