C++11闭包函数的几种实现方法

news2024/9/21 3:34:59

什么是闭包函数

函数就是对传入的一组参数进行运算的行为,闭包函数就是有状态的函数,在参与运算时,除了传入的参数外,还可以对上下文的状态进行运算。类函数运行时就是典型的闭包函数,类函数运行起来后其对象就是状态上下文,函数内部可以使用类对象的数据。

闭包函数的几种实现方式

闭包函数可以采用仿函数、函数绑定和Lambda表达式来实现。

仿函数

仿函数就是将类函数化,即实现类的()运算符,()运算符可以带参数,也可以不带参数。
举例如下:

#include <iostream>
#include <memory>
using namespace std;

class MyFunctor
{
public:
    MyFunctor(int i) : r(i){}//构造函数

    //仿函数,重载operator()
    int operator() (int tmp)
    {
        return tmp+r;
    }
private:
    int r;
};

int main()
{
    MyFunctor obj(10);
    //调用仿函数
    cout << "result=" << obj(1) << endl;

    return 0;
}

执行结果为:
result=11

std::bind实现闭包函数

在介绍std::bind之前,先介绍function模板。

function模板

function模板相当于函数指针,它可以指向全局函数、类中静态函数、仿函数。

#include <iostream>
#include <memory>
#include <functional>

using namespace std;

//1.普通函数
void func()
{
    cout << __func__ <<endl;
}

//2.类中静态函数
class Test
{
  public:
    static int test_func(int a)
    {
        cout << __func__ << "(" << a << ") ->:";
        return a;
    }

};

//3.类中仿函数
class MyFunctor
{
public:
    //仿函数,重载operator()
    int operator() (int a)
    {
        cout << __func__ << "(" << a << ") ->:";
        return a;
    }
};

int main()
{
    //1.绑定普通函数
    function<void(void)> f1=func;
    f1();

    //2.绑定类中静态函数
    function<int(int)> f2=Test::test_func;
    cout << f2(10) << endl;

    //3.绑定类中的仿函数
    MyFunctor obj;
    function<int(int)>  f3=obj;
    cout << f3(33) << endl;

    return 0;
}

执行结果如下:
func
test_func(10) ->:10
operator()(33) ->:33

std::bind绑定全局函数

std::bind可以通过传入的n个参数得到函数对象,然后执行函数。在得到函数对象前,可以明确传递参数;也可以先传递占位符,待执行函数的时候再替换占位符。
举例如下:

#include <iostream>
#include <functional>
using namespace std;

//1.普通函数
void func(int x, int y)
{
    cout << x << ", " << y << endl;
}

int main()
{
    bind(func, 11, 22)();
    bind(func, std::placeholders::_1, std::placeholders:: _2)(22,11);   //22对应前面的_1,11对应前面的_2
    bind(func, std::placeholders::_1, 22)(22,11, 33,44);    //后面的11,33,44没有作用
    bind(func, std::placeholders::_3, 22)(22,11, 33,44);    //后面的11,33,44没有作用

    return 0;
}

执行结果如下:
11, 22
22, 11
22, 22
33, 22

std::bind绑定成员函数

直接上代码:

#include <iostream>
#include <functional>   //std::bind
using namespace std;
using namespace std::placeholders;

class Test
{
public:
    void func(int x, int y)
    {
        //成员函数
        cout << x << "," << y << endl;
    }

    int a;  //成员变量
};

int main()
{
    Test obj;
    //绑定成员函数
    function<void(int,int)> f1=bind(&Test::func, &obj, _1, _2);
    f1(11, 22);

    //绑定成员变量
    function<int &()> f2=bind(&Test::a, &obj);
    f2()=111;   //obj.a=111
    cout<<"obj.a="<<obj.a<<endl;

    return 0;
}

执行结果为:
11,22
obj.a=111

Lambda表达式实现闭包函数

Lambda表达式介绍

lambda 表达式(lambda expression)是一个匿名函数,lambda表达式基于数学中的 λ 演算得名。

C++11中的lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。

lambda表达式的基本构成:
在这里插入图片描述
① 函数对象参数
[],标识一个lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义lambda为止时lambda所在作用范围内可见的局部变量(包括lambda所在类的this)。函数对象参数有以下形式:
 空。没有使用任何函数对象参数。
 =。函数体内可以使用lambda所在作用范围内所有可见的局部变量(包括lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
 &。函数体内可以使用lambda所在作用范围内所有可见的局部变量(包括lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
 this。函数体内可以使用lambda所在类中的成员变量。
 a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
 &a。将a按引用进行传递。
 a, &b。将a按值进行传递,b按引用进行传递。
 =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
 &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
② 操作符重载函数参数
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
③ 可修改标示符
mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
④ 错误抛出标示符
exception声明,这部分也可以省略。exception声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用throw(int)
⑤ 函数返回值
->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
⑥ 是函数体
{},标识函数的实现,这部分不能省略,但函数体可以为空。

class Test
{
public:
    int i = 0;

    void func(int x, int y)
    {
        auto x1 = []{ return i; };          //err, 没有捕获外部变量
        auto x2 = [=]{ return i+x+y; };     //ok, 值传递方式捕获所有外部变量
        auto x3 = [&]{ return i+x+y; };     //ok, 引用传递方式捕获所有外部变量
        auto x4 = [this]{ return i; };      //ok, 捕获this指针
        auto x5 = [this]{ return i+x+y; };  //err, 没有捕获x, y
        auto x6 = [this, x, y]{ return i+x+y; };//ok, 捕获this指针, x, y
        auto x9 = [this]{ return i++; };        //ok, 捕获this指针, 并修改成员的值
    }
};

int main()
{
    int a = 0, b = 1;
    auto f1 = []{ return a; };      //err, 没有捕获外部变量
    auto f2 = [=]{ return a; };     //ok, 值传递方式捕获所有外部变量
    auto f3 = [=]{ return a++; };   //err, a是以赋值方式捕获的,无法修改
    auto f4 = [=]() mutable { return a++; };   //ok, 加上mutable修饰符后,可以修改按值传递进来的拷贝
    auto f5 = [&]{ return a++; };               //ok, 引用传递方式捕获所有外部变量, 并对a执行自加运算
    auto f6 = [a]{ return a+b; };               //err, 没有捕获变量b
    auto f9 = [a,&b]{ return a+(b++); };        //ok, 捕获a, &b
    auto f8 = [=,&b]{ return a+(b++); };        //ok, 捕获所有外部变量,&b

    return 0;
}

值传递和引用传递区别:

int main()
{
    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;

    /*
    运行结果:
        by_val_lambda: 13
        by_ref_lambda: 13
        by_val_lambda: 13
        by_ref_lambda: 14
    */

    return 0;
}

第3次调用结果还是13,原因是由于by_val_lambda中,j被视为了一个常量,一旦初始化后不会再改变。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/61861.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring - @PostConstruct 源码解析

文章目录Prejavax.annotation.PostConstruct 注解源码解析扩展示例Pre Spring Boot - 扩展接口一览 javax.annotation.PostConstruct 注解 Documented Retention (RUNTIME) Target(METHOD) public interface PostConstruct { }严格意义上来说这个并不算一个扩展点&#xff0c…

[附源码]计算机毕业设计校园订餐管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

文献阅读总结(4)Graph convolution machine for context-aware recommender system

上下文感知的图卷积推荐系统 推荐方面的最新进展表明&#xff0c;可以通过在用户-项目交互图上执行图卷积来学习更好的用户和项目表示&#xff0c;然而&#xff0c;该方法在协同过滤(CF)的场景中有一定的局限性&#xff0c;在这种场景中交互上下文是不可用的。 在这项工作中&…

python关联规则学习:FP-Growth算法对药品进行“菜篮子”分析

产品可以根据销售者进行分类 在Evolution上&#xff0c;有一些顶级类别&#xff08;“药品”&#xff0c;“数字商品”&#xff0c;“欺诈相关”等&#xff09;细分为特定于产品的页面。每个页面包含不同供应商的几个列表。 最近我们被客户要求撰写关于关联规则的研究报告&am…

字符串的扩展

字符串解读 es6加强了对Unicode 的支持&#xff0c;允许\uxxxx的形式展现一个字符&#xff0c;例如&#xff1a; console.log(\u0061); // 打印 a\u后面的为字符的 Unicode 码点 \u 后面4位 xxxx 但是这种写法只识别 \u0000 到 \UFFFF 之间的字符&#xff0c;超出需要使用两…

第十七章 管理组件库的pull request

一个好的项目很少会由一个人来独立完成。即使你完成了所有功能实现&#xff0c;也需要有人给你 Review 和提建议、找 Bug。比如添加新的组件、完善文档、添加单元测试、提出改进意见。 这节课我们就介绍一下如何参与开源社区的代码贡献。对于任何一个开源项目&#xff0c;我们…

map容器/multimap容器

目录 1.map基本概念 简介 本质 优点 map和multimap区别 2.map构造和赋值 功能描述: 函数原型 3.map大小和交换 功能描述 函数原型 4 map插入和删除 功能描述 函数原型 5. map查找和统计 功能描述 函数原型 6 map容器排序 学习目标 主要技术点 1.map基本概念…

web入门-爆破

文章目录web21web22web23python脚本php脚本web24web25web26web27web28web21 进入网站&#xff0c;提示要登陆&#xff0c;妥爆破 抓包 注意到最下行的base64编码 发现就是刚刚输入的账号密码 因此这里就是要选择爆破的地方了 发给inruder&#xff0c;添加爆破位 载入题目给…

【坚持不懈的每日一题——力扣篇】1774. 最接近目标价格的甜点成本(中等)-- dfs / dp

GitHub同步更新&#xff08;已分类&#xff09;&#xff1a;Data_Structure_And_Algorithm-Review 公众号&#xff1a;URLeisure 的复习仓库 公众号二维码见文末 以下是本篇文章正文内容&#xff0c;下面案例可供参考。 一、题目描述 力扣今天推的每日一题是道中等题 。 示例…

qt人员管理模块(模块化程序)功能块复制直接使用不冲突

一、前言 qt对人员管理部分个人总结的模块化程序&#xff0c;直接按照步骤复制粘贴程序&#xff0c;直接实现人员管理功能&#xff0c;无需花费脑筋在理清各个思路&#xff0c;适合快速编写组装程序 二、环境 windows qt5.7 sqlite3 三、正文 思来想去大半天&#xff0c;…

JS快速入门

一、JS介绍 JavaScript &#xff08;简称JS&#xff09;&#xff0c;是一门跨平台、面向对象的脚本语言&#xff08;弱类型语言&#xff09;&#xff0c;而Java语言也是跨平台的、面向对象的语言&#xff0c;只不过Java是编译语言&#xff0c;是需要编译成字节码文件才能运行的…

自定义RBAC(1)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; 在对Spring Security稍做了解之后&#xff0c;可以知道&#xff0c;Spring Security其实只是一个实现认证授权的框架&#xff0c;封装了很多实现细节。但也有一些…

【Linux网络编程】服务端编程初体验

文章目录前言服务端是啥、有什么特点核心函数socket的简介服务器编程客户端代码The End前言 在上节课(Linux网络编程初体验)中我们实现了连接bilibili的功能&#xff0c;并获取其html源码 如图所示. 今天我们要自己编写个服务端来服务我们的客户端 提示&#xff1a;以下是本篇…

SGI STL 二级空间配置源码刨析

文章目录内存分配第二级配置器空闲链表的设计内存申请代码内存释放代码注意内存分配 当我们new一个对象时&#xff0c;实际做了两件事情&#xff1a; 使用malloc申请了一块内存。执行构造函数。 在SGI中&#xff0c;这两步独立出了两个函数&#xff1a;allocate申请内存&…

年产20吨鸡枞菌产品的生产工艺设计(lunwen+课题登记表+cad图纸)

目录 摘 要 1 Abstract 2 一、设计任务和内容 4 1.1 设计题目 4 1.2 设计原始数据 4 二、设计说明 5 2.1 全厂总平面布置 5 2.1.1 原料厂及堆场 5 2.1.2 生产区 5 2.1.3 厂前区 6 2.1.4 动力区 6 2.1.5 辅助车间 6 2.1.6 仓库区 6 2.2 三废的处理及回收 6 2.3 车间布置说明 6 三…

Request和Response基础知识入门

文章目录1&#xff0c;Request和Response的概述2&#xff0c;Request对象2.1 Request继承体系2.2 Request获取请求数据2.2.1 获取请求行数据2.2.2 获取请求头数据2.2.3 获取请求体数据2.2.4 获取请求参数的通用方式2.3 IDEA快速创建Servlet2.4 请求参数中文乱码问题2.4.1 POST请…

【Unity3D】绘制物体表面三角形网格

1 仅绘制三角形网格 1&#xff09;创建游戏对象 创建一个空对象&#xff0c;重命名为 Grid&#xff0c;并在其下添加需要绘制网格的对象&#xff0c;如下&#xff1a; 场景显示如下&#xff1a; 2&#xff09;添加脚本组件 GridController.cs using System; using UnityEngin…

JavaWeb_第4章_RequestResponse

JavaWeb_第4章_Request&Response 文章目录JavaWeb_第4章_Request&Response1&#xff0c;Request和Response的概述2&#xff0c;Request对象2.1 Request继承体系2.2 Request获取请求数据2.2.1 获取请求行数据2.2.2 获取请求头数据2.2.3 获取请求体数据2.2.4 获取请求参数…

【ceph】分布式存储ceph

1 块存储&#xff0c;文件存储&#xff0c;对象存储 1.1 简介 文件存储&#xff1a;分层次存储&#xff0c;文件存储在文件夹中&#xff1b;访问文件时系统需要知道文件所在的路径。 举例&#xff1a;企业部门之间运用网络存储器&#xff08;NAS&#xff09;进行文件共享。 …

把握出租车行驶的数据脉搏 :出租车轨迹数据给你答案!

城市化带来的道路拥堵、出行耗时长等交通问题给交管部门带来了巨大的挑战。 ▼ 通过安装在出租车上的GPS设备&#xff0c;可以采集到大量的轨迹数据&#xff0c;从而帮助我们分析人们出行信息&#xff0c;达到优化交通的目的。 最近我们被客户要求撰写关于出租车行驶的研究报…