0


【C++11(二)】lambda表达式和可变参数模板

在这里插入图片描述

一、可变参数模板

C++11的新特性可变参数模板
能够让您创建可以接受
可变参数的函数模板和类模板

// Args是一个模板参数包,args是一个函数形参参数包// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。template<class...Args>voidShowList(Args... args){}

参数包是不支持下标解析的
在这里插入图片描述

1.1 递归函数方式展开参数包

// 递归终止函数voidShowList(){
     cout << endl;}// 展开函数// 增加一个模板参数让编译器自己去解析这个参数包里的东西template<classT,class...Args>voidShowList(const T& value, Args... args){
     cout << value <<" ";ShowList(args...);// 如果是0个参数的参数包就会调用void ShowList()// 如果参数包接收的char类型的参数,会再去调匹配的ShowList函数// 调不到就只能调自己,根据模板推导类型// 打印完,参数包再往下传,0个参数就调用void ShowList()// void ShowList()可以认为是递归终止函数}intmain(){ShowList();// 编译器会找最匹配的,调void ShowList()ShowList(1);// 1传给value,后面的参数包就没有了,参数包代表0-n个参数ShowList(1,'A');// 1传给value,'A'传给参数包ShowList(1,'A', std::string("sort"));return0;}

二、lambda表达式

C++98中
对一个数据集合中的元素排序
可以使用std::sort

如果待排序元素为自定义类型
需要自己写仿函数
如果每次按自定义类型不同的成员变量
进行排序就要写多个仿函数,十分不方便
因此,C++11语法中的Lambda表达式
便是解决此类问题

lambda表达式

intmain(){
 vector<Goods> v ={{"苹果",2.1,5},{"香蕉",3,4},{"橙子",2.2,3},{"菠萝",1.5,4}};sort(v.begin(), v.end(),[](const Goods& g1,const Goods& g2){return g1._price < g2._price;});sort(v.begin(), v.end(),[](const Goods& g1,const Goods& g2){return g1._price > g2._price;});sort(v.begin(), v.end(),[](const Goods& g1,const Goods& g2){return g1._evaluate < g2._evaluate;});sort(v.begin(), v.end(),[](const Goods& g1,const Goods& g2){return g1._evaluate > g2._evaluate;});}

可以发现lambda表达式是一个匿名函数

1.1 lambda表达式语法

书写格式:

[捕捉列表] (参数列表) mutable ->return-type { 函数体 }
  • 捕捉列表:该列表总出现lambda函数开始位置 编译器根据[]判断接下来的代码是否为lambda函数 捕捉列表能够捕捉上下文中的变量供lambda函数使用
  • 参数列表:与普通函数的参数列表一致 如果不需要参数传递,则可以连同()一起省略
  • mutable:默认情况lambda函数总是const函数,mutable可以取消其常量性 使用该修饰符参数列表不可省略(即使参数为空)
  • ->return-type:返回值类型 用追踪返回类型形式声明函数的返回值类型 无返回值可略,有返回值也可略,由编译器推导
  • { 函数体 }:该函数体内除了可使用其参数外 还可以使用所有捕获到的变量

用lambda表达式实现add

auto add =[](int x,int y)->int{return x + y;};// cout << [](int x, int y)->int { return x + y; }(1, 2) << endl; // 这样写比较抽象
cout <<add(1,2)<< endl;auto add2 =[](int x,int y){return x + y;};
cout <<add2(3,2)<< endl;[]{};// 最简单的lambda,捕捉列表和函数体是一定不能省略的

用lambda对自定义类型比较大小

structGoods{
    string _name;// 名字double _price;// 价格int _evaluate;// 评价Goods(constchar* str,double price,int evaluate):_name(str),_price(price),_evaluate(evaluate){}};intmain(){
    vector<Goods> v ={{"苹果",2.1,5},{"香蕉",3,4},{"橙子",2.2,3},{"菠萝",1.5,4}};// 用lambda对自定义类型比较大小// <auto priceLess =[](const Goods& g1,const Goods& g2)->bool{return g1._price < g2._price;};sort(v.begin(), v.end(), priceLess);// >sort(v.begin(), v.end(),[](const Goods& g1,const Goods& g2)->bool{return g1._price > g2._price;});// 比较评价sort(v.begin(), v.end(),[](const Goods& g1,const Goods& g2)->bool{return g1._evaluate > g2._evaluate;});sort(v.begin(), v.end(),[](const Goods& g1,const Goods& g2)->bool{return g1._evaluate < g2._evaluate;});return0;}

捕捉变量

intmain(){int x =1, y =2;auto swap1 =[](int& rx,int& ry){// 只能用当前作用域的变量int tmp = rx;
        rx = ry;
        ry = tmp;};swap1(x, y);
    cout << x <<" "<< y << endl;// 还可以换一种写法// 想用外面的变量,则可以利用捕捉列表进行捕捉(捕捉过来的对象是外面对象的拷贝)/*
    // 传值捕捉
    auto swap2 = [x, y]() mutable // 捕捉多个值用逗号分割即可;直接给值叫做传值捕捉,传值捕捉无法修改,加上mutable(异变)就可以修改
    {
        int tmp = x;
        x = y;
        y = tmp;
    };
    swap2();
    cout << x << " " << y << endl;
    */// mutable用得比较少,建议// 引用捕捉auto swap2 =[&x,&y](){int tmp = x;
            x = y;
            y = tmp;};swap2();
    cout << x <<" "<< y << endl;// 还可以混合捕捉,x引用捕捉,y传值捕捉// 全部引用捕捉auto func1 =[&](){// ...};// 全部传值捕捉auto func2 =[=](){// ...};// 全部引用捕捉,x传值捕捉auto func3 =[&, x](){// ...};return0;}

1.2 函数对象与lambda表达式

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

classRate{public:Rate(double rate):_rate(rate){}doubleoperator()(double money,int year){return money * _rate * year;}private:double _rate;};intmain(){// 函数对象double rate =0.49;
 Rate r1(rate);r1(10000,2);// lamberauto r2 =[=](double monty,int year)->double{return monty*rate*year;};r2(10000,2);return0;}

lambda表达式大小为1个字节
在编译器角度是没有lambda
定义一个lambda
编译器自动生成一个仿函数对象的类型
在该类中重载了operator()
该类是一个空类,空类没给成员变量就是一个字节

函数对象将rate作为其成员变量
在定义对象时给出初始值即可
lambda表达式通过捕获列表可以
直接将该变量捕获到

三、包装器

C++中的function本质是一个类模板
也是一个包装器

intf(int a,int b){return a + b;}structFunctor{public:intoperator()(int a,int b){return a + b;}};intmain(){// int(*pf1)(int, int) = f; // 不是常规的指针类型,写法复杂// 假设要求要声明一个统一的类型// map<string, > // 这里要声明可调用类型,f和Functor调用起来都是一样的,但类型不一样,一个是函数指针一个是类// 这时候就没法声明,而包装器就可以统一封装出可调用类型
    function<int(int,int)> f1 = f;// 返回值加参数包,参数包就是把实际要的类型写上
    function<int(int,int)> f2 =Functor();// Function可以对函数指针和仿函数对象进行包装
    function<int(int,int)> f3 =[](int a,int b){return a + b;};

    cout <<f1(1,2)<< endl;// 包装以后两个类型的对象是一样的
    cout <<f2(2,2)<< endl;
    cout <<f3(3,3)<< endl;

    map<string, function<int(int,int)>> opFuncMap; 
    opFuncMap["函数指针"]= f;
    opFuncMap["仿函数"]=Functor();
    opFuncMap["lambda"]=[](int a,int b){return a + b;};// 包装器的作用:更好的控制可调用对象的类型return0;}

静态成员和非静态成员的包装

classPlus{public:Plus(int rate):_rate(rate){}staticintplusi(int a,int b){return a + b;}doubleplusd(double a,double b){return(a + b)* _rate;}private:int _rate =2;};intmain(){
    function<int(int,int)> f1 = Plus::plusi;// 包装静态成员函数正常包装就可以// 非静态成员函数,就不能这样直接包装// function<double(double, double)> f2 = Plus::plusd;// error C3867: “Plus::plusd”: 非标准语法;请使用 "&" 来创建指向成员的指针// 普通成员函数名代表函数指针.静态成员也一样,指定类域就可取到这个函数指针// 非静态成员需要加一个&// 非静态传参还有一个this指针需要传参// error C2440 : “初始化”: 无法从“double(__cdecl Plus::*)(double, double)”转换为“std::function<double(double, double)>”
    function<double(Plus,double,double)> f2 =&Plus::plusd;// 也可以传Plus*,左值能被取地址,右值不行,匿名对象是右值不能取地址,就不能用匿名对象// 传指针也可以,传对象也可以,因为这不是直接去掉用plusd这个函数,我是一个包装器,f1是直接调用Plusi// f2是用对象去掉用Plusd// 当Plusd是指针,就用指针取调用Plusd// 如果是对象就用对象调用Plusd 

    cout <<f1(1,2)<< endl;
    cout <<f2(Plus(2),20,20)<< endl;// 第一个正常调用,第二个需要加一个匿名对象;需要写一个构造函数,也可以不写,Plus就不能传参// 也可以不用匿名对象
    Plus p1(3);
    cout <<f2(p1,20,20)<< endl;return0;}

bind

调整参数顺序

voidPrint(int a,int b){
    cout << a <<" ";
    cout << b << endl;}intmain(){Print(10,20);// auto RPrint =  bind(Print, placeholders::_2, placeholders::_1); // 第一个参数传可调用对象,_1是一个占位符也是第一个参数,-2是第二个参数以此类推,默认是拿不到的,它放在placeholders命名空间里面
    function<void(int,int)> RPrint =bind(Print, placeholders::_2, placeholders::_1);// 两种写法都可以推荐用autoRPrint(10,20);return0;}

调整参数个数

classSub{public:Sub(int rate):_rate(rate){}intfunc(int a,int b){return(a - b)* _rate;}private:int _rate;};intmain(){
    function<int(Sub,int,int)> fSub =&Sub::func;
    cout <<fSub(Sub(3),10,20)<< endl;
    
    function<int(int,int)> fSub1 =bind(&Sub::func,Sub(3), placeholders::_1, placeholders::_2);
    cout <<fSub1(10,20)<< endl;// 把隐藏this指针绑死就只用传两个参数// 把第二个参数绑死
    function<int(Sub,int)> fSub2 =bind(&Sub::func, placeholders::_1,100, placeholders::_2);// 第二个参数绑死了,第三个参数是_2,还是按顺序挨着走 
    cout <<fSub2(Sub(3),20)<< endl;}

在这里插入图片描述
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见

标签: c++

本文转载自: https://blog.csdn.net/2302_76421042/article/details/135993044
版权归原作者 新梦空间 所有, 如有侵权,请联系我们删除。

“【C++11(二)】lambda表达式和可变参数模板”的评论:

还没有评论