C++11 -- 包装器

news/2025/3/29 18:16:09/

文章目录

  • function包装器
    • function包装器的概念
    • function的运用
    • function实例化
    • 使用function解决逆波兰表达式
  • bind包装器
    • bind包装器相关介绍
    • bind绑定函数固定参数

function包装器

function包装器的概念

function包装器,也叫做适配器,它的本质是一个类模板.
例如:

1 template< class R, class... Args >
2 class function< R (Args...)>

说明
( 1 ) :R是被调用函数的返回类型 Args…是被调用的函数的形参,本质上是一个参数包.

( 2 ): function是类模板,只有成员函数,没有数据成员。

function的运用

function包装器可以对普通函数,函数对象,lambda表达式,类中的成员函数进行包装,以下针对各类函数进行包装调用以及相关注意事项如下:

double f(double a, double b)
{return a + b;
}
struct Functor
{
public:int operator()(int a, int b){return a + b;}
};
class Plus
{
public:static int Plusi(int a, int b)         //静态成员函数{return a + b;}double Plusd(double a, double b)      //成员函数{return a + b;}
};
int main()
{//包装函数指针function<double(double, double)> funcf = f;  //传递函数名cout << funcf(1.1, 2.2) << endl;//包装仿函数function<int(int, int)> funcFunctor = Functor();//传递仿函数对象cout << funcFunctor(11, 22) << endl;//包装lambda表达式function<int(int, int)> funcLmd = [](int a, int b) {return a + b;}; //传递匿名对象cout << funcLmd(11, 22) << endl;//静态成员函数function<int(int, int)> funcPlusi = Plus::Plusi;  //标明类域,传递函数名.cout << funcPlusi(11, 22) << endl;//非静态成员函数function<double(Plus, double, double)> funcPlusd = &Plus::Plusd; //增加&,标明类域,传递函数名.cout << funcPlusd(Plus(),3.3,4.4)<< endl;        //因为类的成员函数需要对象的调用,所以必须传递额外传递一个对象.                        return 0;
}

function实例化

包装器可以解决模板实例化多份而造成的效率低下的问题.
例如:

  • 在useF模板中,有两个参数,第一个参数可以接收各种函数类型,第二个参数可以接收各种信息.
  • useF函数模板中的定义了一个静态变量,如果实例化出多分useF函数模板,静态变量count也将不是同一个变量.

代码如下:

template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count: " << ++count << endl;cout << "count: " << &count << endl;return f(x);
}
double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};
int main()
{//函数指针cout << useF(f, 11.11) << endl;//仿函数cout << useF(Functor(), 11.11) << endl;//lambda表达式cout << useF([](double d)->double {return d / 4; }, 11.11) << endl;return 0;
}

运行结果如下:

  • 由此可见,在这里由于我们对useF函数模板的第一个参数T传递了三种不同类型的函数,进而导致useF函数模板在编译阶段实例化出了三份,因为静态变量为三个不同的变量.
  • 在使用三种不同函数作为实参传递并调用useF函数模板,实际上是分别调用了一次不同的useF函数,进而导致useF函数中的静态变量不是同一个,所以count只增加了一次.
  • 但是由于我们所传的三种函数类型的返回值数据类型和形参数据类型是一样的,在执行useF函数中,其他数据类型也相同,所以根本就没必要实例化三份useF函数.
    在这里插入图片描述
    所以,包装器可以解决因为该情况而导致模板实例化多份造成效率低下的问题.
int main()
{//函数名function<double(double)> func1 = func;cout << useF(func1, 11.11) << endl;//函数对象function<double(double)> func2 = Functor();cout << useF(func2, 11.11) << endl;//lambda表达式function<double(double)> func3 = [](double d)->double {return d / 4; };cout << useF(func3, 11.11) << endl;return 0;
}

运行效果如下:

  • 由于useF类模板中的静态变量相同,由此可以判断经过function包装过后,useF函数模板只实例化出了一份,这极大地提高了函数模板的效率.
  • 当我们传递三种不同的函数类型并进行调用时,实际上就是调用了三次同一个useF函数,对静态变量count累加了三次.
    在这里插入图片描述

使用function解决逆波兰表达式

bind包装器

bind的本质是一个函数模板,它就像一个函数包装器(适配器),可以接收一个可调用对象的,从而生成一个新的可调用对象来"适应"原对象的参数列表.

bind包装器相关介绍

bind原型如下:

// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

注意:
( 1 ): fn 指的是需要包装的对象.

( 2 ): args…对应的是给定fn函数中的参数.

调用bind的一般形式如下:

auto newCallable = bind(callable,arg_list);

( 1 ) callable: 需要包装的对象…

( 2 ) newCallable: 生成的一个新的可调用对象.

( 3 ) arg_list: arg_list是一个逗号分隔的参数列表,对应给定的callable的参数.当我们调用newCallable时,newCallable会调用callable,并将arg_list参数列表传给callable中.

注意:
arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。例如: _1为newCallable的第一个参数,_2为第二个参数,以此类推.

bind绑定函数固定参数

为什么要使用bind绑定固定参数呢?

因为使用bind绑定固定参数后,可以通过bind包装后生成新的形参较少的对象来调用原来的对象.

例如:
在以下例子中,对于成员函数来说,要通过bind包装新生成的对象调用,因为成员函数需要对象才能调用,所以我们要比全局成员函数要额外多传递一个参数,但是当我们使用包装器bind同时包装全局成员函数和普通成员函数时,那么包装器此时根本无法判断接收参数的具体个数.
在这里插入图片描述
代码如下:

int Plus(int a, int b)
{return a / b;
}}
class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{function< int(int, int)> funcPlus = Plus;function< int(int, int)>funcSub = bind(&Sub::sub, Sub(), _1, _2);map<string, function<int(int, int)>> funcMap ={{"+",Plus},{"-",bind(&Sub::sub,Sub(),_1,_2) }};cout << funcSub(2, 1) << endl;cout << funcMap["-"](2, 1) << endl;
}
  • 将成员函中要接收三个参数经过bind适配器绑定后,在定义时只需要在&Sub::sub后面直接传递的一个sub()匿名对象进行该参数绑定,然后在以后的调用时,只需要传递对应的函数所需要的形参就可以了.
  • 这样也完美的解决了bind同时包装类的成员函数和全局函数接收参数不匹配的问题.
    在这里插入图片描述

http://www.ppmy.cn/news/109877.html

相关文章

微服务Spring Cloud 02------使用Eureka实现注册中心(1)

1.Eureka简介 Eureka是Spring Cloud中的一个负责服务注册与发现的组件。遵循着CAP理论中的A(可用性)和P(分区容错性)。 Eureka是Netflix中的一个开源框架。它和 Zookeeper、Consul一样&#xff0c;都是用于服务注册管理的&#xff0c;同样&#xff0c;Spring-Cloud 还集成了Zo…

《Java并发编程实战》课程笔记(四)

互斥锁 原子性问题到底该如何解决呢&#xff1f; “同一时刻只有一个线程执行”这个条件非常重要&#xff0c;我们称之为互斥。如果我们能够保证对共享变量的修改是互斥的&#xff0c;那么&#xff0c;无论是单核 CPU 还是多核 CPU&#xff0c;就都能保证原子性了。 锁模型 …

Python中的魔法函数

魔法函数&#xff08;Magic functions&#xff09;&#xff0c;也称为特殊方法&#xff08;Special methods&#xff09;&#xff0c;是在 Python 中具有特殊名称和双下划线&#xff08;__&#xff09;前缀和后缀的特殊函数。 这些魔法函数允许您定义自定义行为&#xff0c;以…

RocketMQ的demo代码

下面是一个使用Java实现的RocketMQ示例代码&#xff0c;用于发送和消费消息&#xff1a; 首先&#xff0c;您需要下载并安装RocketMQ&#xff0c;并启动NameServer和Broker。 接下来&#xff0c;您可以使用以下示例代码来发送和消费消息&#xff1a; Producer.java文件&…

SpringBoot自动配置原理总结

1、我们需要从主启动类的SpringBootApplication注解开始分析&#xff1a; SpringBootApplication是一个复合注解&#xff0c;进入以后看到主要包括以下三个注解&#xff1a; SpringBootConfiguration EnableAutoConfiguration ComponentScan(excludeFilters { Filter(type …

回归预测 | MATLAB实现SSA-CNN-GRU麻雀算法优化卷积门控循环单元多输入单输出回归预测

回归预测 | MATLAB实现SSA-CNN-GRU麻雀算法优化卷积门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现SSA-CNN-GRU麻雀算法优化卷积门控循环单元多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现SSA-CNN-GRU麻雀算法优…

【22-23 春学期】AI作业11-RNN

1.前馈网络存在的问题 前馈网络是一种单向传递信息的神经网络&#xff0c;它不能处理序列数据&#xff0c;也不能捕捉时间序列数据中的依赖关系。 2.序列数据 序列数据指的是一种按照先后顺序排列的离散数据。只要类似这种一串的数据&#xff0c;前后有顺序关系的数据都叫序…

Linux:centos:周期性计划任务管理《crontab》

crontab常用基础属性 -e 编辑计划任务 -l 查看计划任务 -r 删除计划任务 -u 指定用户的计划任务 首先创建一个名为test的用户名 crontab时间规定 格式&#xff1a;分钟 小时 日期 月份 星期 命令 分钟-- 0-59整数 小时 -- 0-23整数 日期 -- 1--31 整数 月份 -- 1-12 整数 星期…