c++11中的lambda函数

本文转自《深入理解c++11》一书关于lambda函数的使用指南,在此相关的捕获列表知识请读者参考原书对应章节,在结合本文(原书中的示例代码)学习和理解效果更佳

 

#include<iostream>
using namespace std;


/*
int main()
{
    int grils{3},boys{4};
//    auto totalChild=[](int x,int y)->int{return x+y;};    
    auto totalChild=[=](void)->int{return grils+boys;};
//    return totalChild();

    []{};
    int a{3},b{4};
    [=]{return a+b;}; //省略参数列表和返回类型,返回类型由编译器推断为int
    auto fun1=[&](int c){b=a+c;};  //胜利返回值,无返回值
//    fun1(2);
//    cout<<b<<endl; //2
    auto fun2=[=,&b](int c)->int{return b+=a+c;};
     

*/

class _functor{
    public:
        int operator()(int x,int y){
            return x+y;
        }
};
//int main()
//{
//    int grils{3},boys{4};
//    _functor totalChild;
//    return totalChild(4,6);
//}
/*
相比于函数,仿函数可以有初始状态,一般通过class定义私有成员,并在声明对象的时候对其进行初始化。私
有成员的状态就成了仿函数的的初始状态。而由于声明一个仿函数对象可以拥有多个不同初始状态的实例,因此可以
借由仿函数产生多个功能类似却不同的仿函数实例 
*/

class Tax
{
    private:
        float rate;
        int base;
    public:
        Tax(float r,int b):rate(r),base(b){}
        float operator()(float money){return (money-base)*rate;}
        
}; 

//int main()
//{
//    Tax high(0.40,3000);
//    Tax middle(0.25,2000);
//    cout<<"tax over 3w "<<high(37500)<<endl;
//    cout<<"tax over 2w "<<middle(27500)<<endl;
//    //通过带状态的放函数,可以设定两种不同的税率计算 
//}

class AirportPrice
{
    private:
        float _dutyfreerate;
    public:
        AirportPrice(float rate):_dutyfreerate(rate){}
        float operator()(float price){ return price*(1-_dutyfreerate/100);}
};

//int main()
//{
//    
//    float tax_rate=5.5f;
//    AirportPrice changi(tax_rate);
//    
//    auto changi2=[tax_rate](float price)->float{return price*(1-tax_rate/100);};
//    float purchased=changi(3699);
//    float purchased2=changi2(2899);
//    //lambda函数捕获tax_rate,仿函数tax_rate初始化类。其他的,如在参数传递上,两者保持一致。
//    //除去在语法层面上的不同,lambda和仿函数却有着相同的内涵->都可以捕获一些变量作为初始状态,并接受参数
//    //进行运算 
//}

/*
事实上,仿函数是一种编译器实现lambda函数的一种方式。在现阶段,通常编译器都会吧lambda函数转化为一个仿函数对象。
因而,c11中,lambda可以视为仿函数的一种等价方式


lambda函数在c++11标准中是内联的 
*/
/*
lambda函数的一些问题 

使用lambda函数是,捕获列表不同会得到不同的结果
对于按值方式传递的捕获列表,其传递的值在lambda函数定义的时候已经决定了
对于按原样传递的捕获列表变量,其传递的值则等于lambda函数调用时候的值 
*/

//int main() 
//{
//    int j=12;
//    auto by_value_lambda=[=]{return j+1;};
//    auto by_ref_lambda=[&]{return j+1;};
//    
//    cout<<"by_value_lambda: "<<by_value_lambda()<<endl; 
//    cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;
//    
//    j++;
//    cout<<"by_value_lambda: "<<by_value_lambda()<<endl; //13
//    cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;
//}

/*
总结:
在使用lambda函数的时候,如果需要捕获的值成为lambda函数的常量,我们通常会按值传递的方式捕获。
需要捕获的值成为Lambda函数运行时的变量(类似于参数),则应该采用按引用的方式进行捕获。


lambda和函数指针的关系
大多数情况下,将匿名的Lambda赋值给了auto变量,这是lambda函数的一种声明和使用Lambda函数的方法
结合之前与仿函数的对比,lambda并非自定义类型或者是函数指针

从c++11的定义上发现,lambda类型被定义为闭包的类(c++11标准规定,closure类型被定义为特有的unique,匿名且非联合体的class类型·),而每个lambda表达式则会产生一个闭包类型的临时对象(右值)。
严格上讲,lambda函数并非函数指针。只不过c++11标准运行lambda表达式向函数指针的转换,
前提在于:
     lambda没有捕获任何变量,且函数指针所指示的函数原型,必须跟lambda函数右值相同的调用方式。 
*/

//int main() 
//{
//    int grils{3},boys{4};
//    auto totalChild=[](int x,int y)->int{return x+y;};
//    typedef int (*allChild)(int x,int y);
//    typedef int (*oneChild)(int x);
//    
//    oneChild p;
    p=totalChild; //编译失败,参数必须一致 
//
//    decltype(totalChild) allPeople=totalChild; //需提供decltype获得lambda的类型
    decltype(totalChild) totalPeple=p; //编译失败 指针无法转换为lambda 
//}

/*
将函数指针转化为lambda是不成功的

程序员可以通过decltype的方式获取Lambda函数的类型,一般用在实例化一些模板的时候使用 
*/


/**

lambda的常量性和mutable关键字 
*/

int main()
{
    int value=0;
    //编译失败,在const的lambda中修改常量 
    //auto const_val_lambda=[=](){value=3;};
    //非const的lambda,可以修改常量数据 
    auto const_val_lambda=[=]()mutable{value=3;};
    cout<<value<<endl;
    //依旧是const的lambda,不过没有改动引用本身 
    auto const_ref_lambda=[&](){value=3;};
    cout<<value<<endl;
    //依旧是const的lambda,通过参数传递value 
    auto const_param=[&](int v){v=3;};
    const_param(value);
 } 
 
 /*
 c11中·,默认情况下,Lambda函数是一个const函数,按照规则,一个const函数是不能在函数体中改变非静态成员变量的
 。
 但是在此处,编译器对于不同传参或者捕获列表的lambda函数执行了不同的规则,如何理解?
 
 
 回顾之前lambda函数与仿函数基本的一致,或者就是原则,
 我们需要将lambda函数转化为一个完整的仿函数,lambda函数的函数体部分,被转化仿函数以后将成为一个class的常成员函数
 
 整个const_val_lambda看起来会像是下面的样子
  

 
class const_val_lambda
{
    public:
    const_value_lambda(int v):val(v){
    }     
    public:
    void operator()() const{ val=3;};  //常成员函数 
    private:
        int val;
}; 


具体来讲,对于出来成员函数,不能在函数体内改变class中的任何成员变量。


有上面的实验之后,我们可以得出以下结论

Lambda的捕捉列表中的变量会成为等价仿函数的成员变量,而常量成员函数(如operator() 中改变其值的语义是不允许
的,因而按值捕获的变量在没有声明为mutable的lambda中,其值一旦被修改就会导致编译器报错


对于引用方式传递的变量在常量函数中的值被更改则不会导致错误。---》由于函数const_ref_lambda不会改变引用本身,
只会改变引用的值,因此编译器将编译通过。


现有的c+11标准中的lambda等价的是有常量operator()的仿函数。--》在使用不会列表的时候,按值捕获的变量是lambda
中不可改变的。

这个设计有其合理性,改变从上下文中拷贝的临时变量通常布局任何意义。
绝大多数时候,临时变量知识用于lambda函数的输入,如需输出到上下文,可以使用引用,或者让lambda函数放回的值来实现
 


 

 


 
) 
 */

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值