C++11之lambda函数

2023-05-16

最近一直在看mesos的源代码,mesos中用到了很多C++11的新特性,lambda函数就是其中的一个。对于lambda函数简单的来说就是java中的匿名函数。

语法定义

[capture] (paramenters) mutable->return-type {statement}

  1. [capture] 捕捉列表:捕捉上下文中的变量供函数使用
  2. (paramenters) 参数列表:跟普通参数列表相同,如果没有参数则可以连同()一起省略
  3. mutable mutable修饰符:默认情况下,lanbda函数是一个const函数,mutable关键字可以取消其常量性,此时,即使参数列表为空,也不可以省略参数列表
  4. ->return-type 返回类型:在没有返回值时可以连同->一起省略,同时,在返回类型明确的情况下也可以省略,由编译器进行推导
  5. {statement} 函数体:除了参数之外还可以使用捕捉的变量

简单的lambda函数例子:

#include <iostream>
using namespace std;
int main( int argc, char* argv[]  )
{
    int boys = 3;
    int girls = 4;

    []{};//最简单的lambda函数

    [=]{ return boys + girls; };//省略参数列表和返回类型

    auto fun1 = [&]( int c ){ girls = boys + c; };

    auto fun2 = [=, &girls]( int c )->int { return girls += boys + c; };

    auto totalChild = [ girls, &boys ]()->int { return girls + boys; };
    cout << totalChild();
    return 0;
}

捕捉列表的语法规则

  1. [var]表示值传递的方式捕捉变量
  2. [=]b表示值传递的方式捕捉所有的父作域的变量(包括this)
  3. [&var]表示引用传递捕捉变量
  4. [&]表示引用传递捕捉所有父作用域变量(包括this)
  5. [this]表示值传递的方式捕捉当前的this指针

    举例:
    [=,&a,&b] 表示以引用传递的方式捕捉变量a和b,以值传递的方式捕捉其他变量
    [&,a,this]表示以值传递的方式捕捉变量a和this,以引用传递的方式捕捉其他变量

注意

不可以重复捕捉相同的变量
举例:
[&,&a]变量a被捕捉两次,这样不可以
在块作用域(在{}内的任何代码都是有块作用域的)以外的lambda函数捕捉列表必须为空

lambda与仿函数

仿函数:重定义了成员函数operator()函数的一种自定义的类型对象,在代码层面使用起来跟函数没有区别。

仿函数的例子:

#include <iostream>

using namespace std;

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( int argc, char* argv[]  )
{
    Tax high( 0.4, 3000 );
    Tax middle( 0.25, 2000 );

    cout << "tax over 3w: " << high( 37500 ) << endl;
    cout << "tax over 2w: " << middle( 27500 ) << endl;
    return 0;
}

在通过一个例子介绍仿函数与lambda函数的对比:

#include <iostream>
using namespace std;
class AirportPrice
{
private:
    float _dutyfreerate;

public:
    AirportPrice( float price ):_dutyfreerate( price ){  }
    float operator() ( float price ){ return price * ( 1 - _dutyfreerate / 100 ); }
};
int main( int argc, char *argv[]  )
{
    float tax_rate = 5.5f;
    AirportPrice Change1( tax_rate );

    auto Change2 = [ tax_rate ]( float price )->float { return price * ( 1 - tax_rate / 100 ); };

    float purchased1 = Change1( 3699 );
    float purchased2 = Change2( 2899 );

    cout << purchased1 << endl;
    cout << purchased2 << endl;
    return 0;
}

通过上面的例子可以发现仿函数与lambda函数在使用上一样,并且都可以捕捉一些变量作为初始状态,而事实上,编译器在编译阶段是将lambda函数转化成仿函数对象。

基础使用

1.在使用效果上,仿函数等价于一个局部函数。对于运算比较复杂的函数,通常函数中会有大量的局部状态和变量,这时函数需要实现一些“局部”功能,例如打印信息等,而这些局部功能不能与其他的函数共享,但是需要在此函数中重复使用,那么使用lambda的捕捉列表功能相比于较独立的全局静态函数或者私有成员函数方便很多。

2.在编写程序时,开发者通常会发现自己需要一些“常量”,但是这些常量的值却由自己初始化状态决定,例如:

#include <iostream>
using namespace std;
int Prioritize( int );
int allworks( int times  )
{
    int i;

    int x;
    try{
        for( i = 0; i < timesl i++  )
            x += Prioritize(i);//直接操作x函数
    }
    catch(...)
        x = 0;

    const int y = [=]{
        int i, val;
        try{
            for( i = 0; i < times; i++ )
                val += Prioritize(i);
        }
        catch(...)
            val = 0;
        return val;
    }();
}

其他

值传递捕捉列表与引用传递捕捉列表

对于按值方式传递的捕捉列表,其传递的值在lambda函数定义的时候就决定了;按引用传递的捕捉列表变量,其传递的值等于lambda函数调用时的值。
代码例子:

#include <iostream>
using namespace std;

int main( int argc, char* argv[] )
{
    int j = 12;

    auto by_val_lambda = [=] { return j + 1;  };
    auto by_ref_lambda = [&] { return j + 1;  };

    cout << " by_val_lambda: " << by_val_lambda() << endl;
    cout << " by_ref_lambda: " << by_ref_lambda() << endl;

    j++;

    cout << " by_val_lambda: " << by_val_lambda() << endl;
    cout << " by_ref_lambda: " << by_ref_lambda() << endl;
    return 0;
}

运行结果如下:

 by_val_lambda: 13
 by_ref_lambda: 13
 by_val_lambda: 13
 by_ref_lambda: 14

lambda函数与函数指针之间的转换

C++11允许lambda函数向函数指针转换,但是要求lambda函数没有捕捉到任何变量,且函数指针所示的函数原型,必须跟lambda函数有着相同的调用方式;同时,C++11不允许函数指针转化成lambda函数。
代码例子:

#include <iostream>

using namespace std;

int main( int argc, char* argv[] )
{
    int girls = 3;
    int boys = 4;

    typedef int (*allChild)( int x, int y );
    typedef int (*oneChild)( int x );

    auto totalChild = [](int x, int y)->int { return x + y ; };

    allChild p;
    p = totalChild;

    /*编译失败
    oneChild q;
    q = totalChild;
    */

    decltype( totalChild ) allPeople = totalChild;//通过decltype函数获得lambda函数的类型
    //编译失败
    //decltype( totalChild ) totalPeople = p;
    cout << (*p)( 5, 5 ) << endl;
    return 0;
}

mutable关键字

在C++11中。默认情况下lambda函数是一个const函数,按照规则const的成员函数不能在任何的函数体中改变非静态成员变量的值。

代码例子:

#include<iostream>
using namespace std;
int main( int argc,  char* argv[]  )
{
    int val = 10;

    /*
     * 编译失败
    auto const_val_lambda = [=]() { val = 3 };
    */
    //非const得lambda,可以修改常量数据
    auto mutable_val_lambda = [=]() mutable{ val = 3; cout << "lambda val = " << val << endl; };
    mutable_val_lambda();
    cout << "val = " << val << endl;

    //依然是const的lambda,不过没有改动引用本身
    auto const_ref_lambda = [&]() { val = 3;  cout << "lambda val = " << val << endl;};
    const_ref_lambda();
    cout << "val = " << val <<  endl;

    //依然是const的lambda,通过参数传递val
    auto const_param_lanbda = [&]( int v ){ v = 13; cout << "lambda val = " << v << endl; };
    const_param_lanbda( val  );
    cout << "val = " << val << endl;

    return 0;
}

运行结果:

lambda val = 3
val = 10
lambda val = 3
val = 3
lambda val = 13
val = 3

其实编译器将上述代码中的const_val_lambda转化成仿函数,代码如下:

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

从上述的代码来看,准确地说,lambda函数等价于const类型的operator()的仿函数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11之lambda函数 的相关文章

随机推荐

  • Nginx学习(7)—— 过滤模块(filter)

    文章目录 过滤模块简介执行时间和内容执行顺序Nginx是怎么按照次序依次来执行各个过滤模块的呢这些过滤模块的简述 xff08 按执行顺序 xff09 模块编译过滤模块分析相关结构体 响应头过滤函数响应体过滤函数主要功能介绍发出子请求优化措施
  • Nginx学习(8)—— upstream模块

    文章目录 upstream模块简介upstream模块接口memcached模块分析小结 upstream模块简介 nginx模块一般被分成三大类 xff1a handler filter和upstream 前面的章节中 xff0c 读者已
  • Nginx学习(9)—— 负载均衡模块

    文章目录 Nginx负载均衡模块负载均衡配置指令钩子初始化配置初始化请求peer get和peer free回调函数 小结 Nginx负载均衡模块 负载均衡模块用于从 upstream 指令定义的后端主机列表中选取一台主机 nginx先使用
  • Nginx学习(10)—— event模块、core模块、变量

    文章目录 core模块Nginx启动模块 event模块event的类型和功能accept锁 定时器变量Nginx中的变量指的是什么Nginx中如何创建变量Nginx中如何使用变量举个例子 Nginx的模块种类有很多 xff0c 除了HTT
  • Nginx学习(11)—— Nginx源码架构、configure是怎么执行的(编译的具体细节)

    文章目录 Nginx的源码目录结构Nginx中configure的原理auto脚本 模块编译顺序 Nginx的源码目录结构 nginx的源码目录与nginx的模块化以及功能的划分是紧密结合 xff0c 这也使得我们可以很方便地找到相关功能的
  • C语言中命令行工具 (getopt和getopt_long)

    span class token macro property span class token directive hash span span class token directive keyword include span spa
  • C#与Java的比较

    关于java和C 争论不是一年两年了 贬低C 的文章也看过了很多 说一下个人见解 C 本身并不是不如java C 的优势在于学了之后在web和winform之间可以自由切换 web现在 net mvc已经效率很高了 企业级的应用现在很受欢迎
  • vs2010 和 vs2012同时安装遇到的问题

    安装VS2012后遇到的问题 悲剧的种子是在上个月初种下的 9月份微软发布了Visual Studio2012 xff08 发布会 xff09 xff0c 我是个对各种 新版本 极有偏好的人 xff0c 一看到新闻就立刻下载了VS2012
  • React警告:Received NaN for the `children` attribute. If this is expected, cast the value to a string.

    使用React框架时 xff0c 组件在使用 lt span gt Math abs goal goalInfo pretimes goal usergoalInfo cpt times lt span gt 这一语句时出现警告 xff1a
  • 持续请求/socket.io/?EIO=3&transport=polling&t=N8HrzIR

    项目基本介绍 xff1a 使用React xff0c webpack xff0c socket io client Node js Express socket io 等技术 xff0c 采用前后端分离开发 实现项目中的聊天室时遇到报错 x
  • node-xlsx 生成并下载有超链接的excel文件

    需求 xff1a 将微信小程序云数据库中的数据导出为excel文件 xff0c 文件按团队分为不同的sheet页 xff0c 首页汇总每个sheet页的数据总数 xff0c 并可点击跳转至对应的sheet页 下载时可选择今年某月份进行下载对
  • Vue通过v-for渲染的元素与$refs得到的实例对应不上

    开发时遇到一个bug xff1a 通过v for渲染出几个搜索条件组件 xff08 对应的数组数据记为selectList xff09 xff0c 通过其他方式修改了这些筛选条件对应的数据selectList xff0c 之后通过 refs
  • iView的Select 选择器选择失效

    问题 xff1a 给iView的Select赋的值通过接口获取 xff0c 得到数组 list xff0c 选择器的默认值 defaultValue 为数组list的第一个选择项 xff08 defaultValue 61 list 0 x
  • 不同路由对应同一组件页面

    在vue中 xff0c 当不同路由对应同一组件页面时会发生再次进入页面时不再重新渲染 xff08 为了更高效 xff0c 所以vue进行了复用 xff09 的问题 xff0c 整理一下解决办法 xff0c 如下 xff1a 方式一 Watc
  • /deep/样式穿透失效的原因和解决办法

    问题 xff1a vue页面中 xff08 样式使用less书写 xff09 xff0c 对iview的组件使用 deep 进行样式穿透修改默认样式 xff0c 发现在Google Chrome版本64上看样式修改成功 xff0c 但在火狐
  • 错误:ERROR in ./node_modules/_webpack-dev-server...Module not found: Error: Can't resolve 'webpack/hot

    学习webpack的途中总是困难重重 使用webpack dev server工具时 xff0c 运行cnpm run dev后报错 xff1a ERROR in node modules webpack dev server 64 2 1
  • 带参数的宏定义与有参函数的区别

    1 先介绍一下什么是宏定义 宏定义属于C语言编译系统中编译预处理中的一部分 xff08 但编译预处理不是C语言的语句 xff09 xff0c 其作用是为编译系统提供必要的前置信息 xff0c 告诉编译系统在源程序进行编译之前应该做些什么 它
  • UART接口控制器-RS-232的9脚接口

    RS 232常见引脚信号的定义 RXD 接收数据 xff0c TXD 发送数据 xff0c DTR 数据终端准备 xff0c GND 信号地 xff0c DSR 数据设备准备好 xff0c RTS 请求发送 xff0c CTS 清除发送 串
  • 去掉字符串最后一个字符的方法

    C 开发过程中一般都需要进行字符串的格式化处理 xff0c xff0c 以下提供去掉字符串最后一个字符的方法 如果是其他语言开发的话仅供参考有可能写法不一样 xff0c 但是意思是一样的 字符串 xff1a string s 61 34 1
  • C++11之lambda函数

    最近一直在看mesos的源代码 xff0c mesos中用到了很多C 43 43 11的新特性 xff0c lambda函数就是其中的一个 对于lambda函数简单的来说就是java中的匿名函数 语法定义 capture paramente