手把手教你实现一个function模板

news2024/11/26 12:19:55

1.实现function需要用到的相关技术

建议看本文之前,需要先了解C++11 function或者boost::function模板的基本用法,也最好看一下我的另外一篇文章:
c++11 function模板:模板特化与可变参数函数模板

如果你使用过C++11 function模板或者boost :: function, 你一定会惊讶于这个模板对函数回调带来的方便,可能也会好奇这个功能到底是怎么实现的。我觉得还是有必要研究一下背后的实现原理,因为这背后的实现用到了很多C++语言编程骚操作(语言特性和技巧)。主要体现在如下几个方面:

  • 函数对象(仿函数)
  • 虚函数与多态
  • 模板技术
  • 函数重载
  • 不定模板参数

2.function实现

2.1 公共接口的实现

实现一个抽象基类,作为调用的接口,然后从这个积累派生出几个不同的子类,从而实现对不同形式的调用。考虑到调用的函数的参数是不确定的,因此采用了不定模板参数。

// 模板参数R 表示函数的返回值类型
// 模板参数Arg 表示函数的入参类型
template <typename R, typename... Arg> 
class invoker_base {
public:
  virtual R operator()(Arg... arg)=0;
};

2.2 实现对普通函数的调用

template <typename R, typename... Arg> 
class function_ptr_invoker 
  : public invoker_base<R,Arg...> {
    typedef R (*funcType)(Arg...);
    funcType func_;
public:
    function_ptr_invoker(funcType func):func_(func) {}
    // 重写基类的调用接口,用于对普通函数的调用
    R operator()(Arg... arg) {
        return (func_)(arg...);
    }
};

2.3 实现对函数对象的调用

// 处理函数对象的版本
template <typename R, typename Arg, typename T> 
class function_object_invoker : 
  public invoker_base<R,Arg> {
    T t_;
public:
    function_object_invoker(T t):t_(t) {}
	// 重写基类的调用接口,用于对函数对象调用
    R operator()(Arg arg) {
        return t_(arg);
    }
};

2.4 实现对成员函数的调用

// 实现对类成员函数的调用
template <typename R, typename T, typename... Arg> 
class member_ptr_invoker : 
    public invoker_base<R,Arg...> {
    typedef R (T::*memfuncType)(Arg...);
    memfuncType func_;
    T* t_;
public:
    member_ptr_invoker(memfuncType func,T* t)
        :func_(func),t_(t) {}
	// 重写基类的调用接口,用于对类成员函数的调用
    R operator()(Arg... arg) {
        return (t_->*func_)(arg...);
    }
};

2.5 对不同类型函数调用的测试

到目前为止,通过虚函数,实现了对普通函数、函数对象以及类成员函数的统一调用接口了,可以通过如下方法实现对不同类型的统一调用:

// 根据pBase指向的对象的不同,调用不同的函数。
// pBase可以指向保存普通函数的对象、保存类成员函数的对象或
// 保存函数对象的对象。
pBase->operator()("hello");

完整代码如下:

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

// 实现一个抽象基类,作为调用的接口,然后从这个积累派生出几个不同的子类,从而实现对不同形式的调用
template <typename R, typename... Arg> class invoker_base {
public:
    virtual R operator()(Arg... arg)=0;
};

template <typename R, typename... Arg> class function_ptr_invoker 
  : public invoker_base<R,Arg...> {
    typedef R (*funcType)(Arg...);
    funcType func_;
public:
    
    function_ptr_invoker(funcType func):func_(func) {}

    R operator()(Arg... arg) {
        return (func_)(arg...);
    }
};
// 处理函数对象的版本
template <typename R, typename T, typename... Arg> 
class function_object_invoker : 
  public invoker_base<R,Arg...> {
    T t_;
public:
    function_object_invoker(T t):t_(t) {}

    R operator()(Arg... arg) {
        return t_(arg...);
    }
};

// 实现对类成员函数的调用
template <typename R, typename T, typename... Arg> 
class member_ptr_invoker : 
    public invoker_base<R,Arg...> {
    typedef R (T::*memfuncType)(Arg...);
    memfuncType func_;
    T* t_;
public:
    member_ptr_invoker(memfuncType func,T* t)
        :func_(func),t_(t) {}

    R operator()(Arg... arg) {
        return (t_->*func_)(arg...);
    }
};
/*   用于测试的函数 */
// 测试普通函数
bool some_function(const std::string s) {
  std::cout << s << " This is a common function \n";
  return true;
}
// 测试类成员函数
class some_class {
public:
  bool some_function(const std::string s) {
    std::cout << s << " This is a member function \n";
    return true;
  }
};
// 测试函数对象
class some_function_object {
public:
  bool operator()(const std::string s) {
    std::cout << s << 
      " This is a function object \n";
    return true;
  }
};



int main()
{
    invoker_base<bool, string>* pBase;
    // 调用类成员函数
    some_class s;
    pBase = new member_ptr_invoker<bool, some_class, string>(some_class::some_function, &s);
    pBase->operator()("hello");

    // 调用普通函数
    pBase = new function_ptr_invoker<bool, string>(some_function);
    pBase->operator()("hello");

    // 调用函数对象
    some_function_object func_obj;
    pBase = new function_object_invoker<bool, some_function_object, string>(func_obj);
    pBase->operator()("hello");
    return 0;
}

打印结果:
在这里插入图片描述

2.6 对函数接口进行包装

截止到一步,显然离我们最终的目标还有些差距。虽然到目前为止,我们可以将基类指针指向不同的对象,从而实现对不同类型函数调用,但是我们最终的目的是希望模板能够根据我们传入参数的类型(普通函数、函数对象或者是成员函数)能够进行自动创建相应的对象。于是,我们要需要对接口做一些包装工作。这里主要需要用到模板的重载技术。

template <typename R, typename Arg> class function1 {
  invoker_base<R,Arg>* invoker_;
public:
  function1(R (*func)(Arg)) : 
  invoker_(new function_ptr_invoker<R,Arg>(func)) {}

  template <typename T> function1(R (T::*func)(Arg),T* p) : 
    invoker_(new member_ptr_invoker<R,Arg,T>(func,p)) {}

  template <typename T> function1(T t) : 
    invoker_(new function_object_invoker<R,Arg,T>(t)) {}

  R operator()(Arg arg) {
    return (*invoker_)(arg);
  }

  ~function1() {
    delete invoker_;
  }
};

包装之后测试如下:
注意对比与包装之前的区别。可以看到对接口包装之后,可以根据我们传入的参数类型自动创建相关类型的对象,不再需要我们自己根据类型去创建具体的不同子类对象。

invoker_base<bool, string>* pBase;
    // 调用类成员函数
    some_class s;
    // pBase = new member_ptr_invoker<bool, some_class, string>(some_class::some_function, &s);
    // pBase->operator()("hello");
    function1<bool, string> memfunc(some_class :: some_function, &s);
    memfunc("hello");

    // 调用普通函数
    // pBase = new function_ptr_invoker<bool, string>(some_function);
    // pBase->operator()("hello");
    function1<bool, string> commonfunc(some_function);
    commonfunc("hello");
    // 调用函数对象
    some_function_object func_obj;
    // pBase = new function_object_invoker<bool, some_function_object, string>(func_obj);
    // pBase->operator()("hello");
    function1<bool, string> objfunc(func_obj);
    objfunc("hello");

2.8 对接口做一些调整

不知道有没有注意到,c++11或boost::function中对模板参数的传入类型如下:

// 注意 <>内的形式 
function<bool(string)> f;

但是我们上面实现的function1模板参数的传入类型是下面这样的:

function<bool, string> f;

显示第一种形式更直观,所以我们还得最我们的实现做一些调整。
主要需要用到模板特化技术。

// 定义一个主模板
template <typename R, typename... Arg> 
class function1;
// 然后对模板参数进行特化
template <typename R, typename... Arg> 
class function1<R(Arg...)> {
   // 特化模板中的其它实现完全不变
};

经过上面的调整之后,就可以按照下面的方式使用模板了:

	// 调用类成员函数
    some_class s;
    function1<bool(string)> memfunc(some_class :: some_function, &s);
    memfunc("hello");

    // 调用普通函数
    function1<bool(string)> commonfunc(some_function);
    commonfunc("hello");

    // 调用函数对象
    some_function_object func_obj;
    function1<bool(string)> objfunc(func_obj);
    objfunc("hello");

附 完整代码

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

// 实现一个抽象基类,作为调用的接口,然后从这个积累派生出几个不同的子类,从而实现对不同形式的调用
template <typename R, typename... Arg> class invoker_base {
public:
    virtual R operator()(Arg... arg)=0;
};

template <typename R, typename... Arg> class function_ptr_invoker 
  : public invoker_base<R,Arg...> {
    typedef R (*funcType)(Arg...);
    funcType func_;
public:
    
    function_ptr_invoker(funcType func):func_(func) {}

    R operator()(Arg... arg) {
        return (func_)(arg...);
    }
};
// 处理函数对象的版本
template <typename R, typename T, typename... Arg> 
class function_object_invoker : 
  public invoker_base<R,Arg...> {
    T t_;
public:
    function_object_invoker(T t):t_(t) {}

    R operator()(Arg... arg) {
        return t_(arg...);
    }
};

// 实现对类成员函数的调用
template <typename R, typename T, typename... Arg> 
class member_ptr_invoker : 
    public invoker_base<R,Arg...> {
    typedef R (T::*memfuncType)(Arg...);
    memfuncType func_;
    T* t_;
public:
    member_ptr_invoker(memfuncType func,T* t)
        :func_(func),t_(t) {}

    R operator()(Arg... arg) {
        return (t_->*func_)(arg...);
    }
};
/*   用于测试的函数 */
// 测试普通函数
bool some_function(const std::string s) {
  std::cout << s << " This is a common function \n";
  return true;
}
// 测试类成员函数
class some_class {
public:
  bool some_function(const std::string s) {
    std::cout << s << " This is a member function \n";
    return true;
  }
};
// 测试函数对象
class some_function_object {
public:
  bool operator()(const std::string s) {
    std::cout << s << 
      " This is a function object \n";
    return true;
  }
};
template <typename R, typename... Arg> 
class function1;

template <typename R, typename... Arg> 
class function1<R(Arg...)> {
    invoker_base<R,Arg...>* invoker_;
public:
    // 对普通函数进行重载
    function1(R (*func)(Arg...)) : 
    invoker_(new function_ptr_invoker<R,Arg...>(func)) {}
    
    // 对类成员函数进行重载
    template <typename T> 
    function1(R (T::*func)(Arg...),T* p) : 
        invoker_(new member_ptr_invoker<R,T, Arg...>(func,p)) {}
    
    // 对函数对象进行重载
    template <typename T> 
    function1(T t) : 
        invoker_(new function_object_invoker<R,T, Arg...>(t)) {}
    
    // 对接口进行包装
    R operator()(Arg... arg) {
        return (*invoker_)(arg...);
    }

  ~function1() {
    delete invoker_;
  }
};

int main()
{
   
    // 调用类成员函数
    some_class s;
    function1<bool(string)> memfunc(some_class :: some_function, &s);
    memfunc("hello");

    // 调用普通函数
    function1<bool(string)> commonfunc(some_function);
    commonfunc("hello");

    // 调用函数对象
    some_function_object func_obj;
    function1<bool(string)> objfunc(func_obj);
    objfunc("hello");
    return 0;
}

参考资料:
1、boost::function用法详解
2、关于c++11 std::function的模板参数 <_Res(_ArgTypes…)>

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

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

相关文章

关于Linux内核中的异步IO的使用

我们都知道异步IO的作用&#xff0c;就是可以提高我们程序的并发能力&#xff0c;尤其在网络模型中。在linux中有aio的一系列异步IO的函数接口&#xff0c;但是这类函数都是glibc库中的函数&#xff0c;是基于多线程实现&#xff0c;不是真正的异步IO&#xff0c;在内核中有真正…

生成无限制微信小程序码

生成无限制的微信小程序码&#xff0c;主要是通过后端请求微信的接口&#xff0c;然后微信会把小程序码返回来。 本文不讲详细的方法了&#xff0c;只讲其中的一些关键点&#xff0c;官方文档也附上去了&#xff0c;结合这些点看官方文档会比较方便。 方法&#xff1a; 获取…

_8LeetCode代码随想录算法训练营第八天-C++字符串

_8LeetCode代码随想录算法训练营第八天-C字符串 28.实现strStr()459.重复的字字符串 28.实现 strStr() KMP算法 什么是KMP 是由三位学者发明的&#xff1a;Knuth&#xff0c;Morris和Pratt&#xff0c;所以取了三位学者名字的首字母。 KMP有什么用 KMP主要应用在字符串匹…

SuperMap iClient3D for WebGL/Cesium端性能优化

目录 一、请求优化 1.1 多子域 1.1.1 scene.open()打开场景 1.1.2 加载地形 1.1.3 加载影像 1.1.4 加载S3M 1.1.5 加载MVT 1.2 批量请求 1.2.1 地形 1.2.2 影像 二、内存优化 2.1 根节点驻留内存 2.2 自动释放缓存 2.3 内存管理 三、图层优化 3.1 LOD 3.2 空间索引 3.3 控制图层…

企业文件泄漏防不胜防?安全防护3步走!

有一些管理者认为公司从未曾发生过数据泄密事件而心存侥幸&#xff0c;但数据泄密的代价&#xff0c;只需发生过一次&#xff0c;就足以给企业带来巨大的损害。 十四五规划中&#xff0c;“数据安全”和“网络安全”多次出现&#xff0c;加上《数据安全法》、《个人信息保护法》…

Linux下printf输出字符串的颜色

基本打印格式: printf("\033[字背景颜色;字体颜色m字符串\033[0m" ); printf("\033[41;32m字体背景是红色&#xff0c;字是绿色\033[0m\n"); 41是字背景颜色, 32是字体的颜色, 字体背景是红色&#xff0c;字是绿色是要输出的字符串. 后面的\033 ...\033[0m…

推荐系统毕业设计 协同过滤商品推荐系统设计与实现

文章目录1 简介2 常见推荐算法2.1 协同过滤2.2 分解矩阵2.3 聚类2.4 深度学习3 协同过滤原理4 系统设计4.1 示例代码(py)5 系统展示5.1 系统界面5.2 推荐效果6 最后1 简介 &#x1f525; Hi&#xff0c;大家好&#xff0c;这里是学长的毕设系列文章&#xff01; &#x1f525…

redis持久化方案介绍

Redis有两种持久化方案&#xff1a;1. RDB持久化 2. AOF持久化 1 RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#xff0c;也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后&…

[附源码]Python计算机毕业设计高校实习管理平台系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

【自然语言处理】基于TextRank算法的文本摘要

基于TextRank算法的文本摘要文本摘要是自然语言处理&#xff08;NLP&#xff09;的应用之一&#xff0c;一定会对我们的生活产生巨大影响。随着数字媒体的发展和出版业的不断增长&#xff0c;谁还会有时间完整地浏览整篇文章、文档、书籍来决定它们是否有用呢&#xff1f; 利用…

数据结构C语言版 —— 链表增删改查实现(单链表+循环双向链表)

文章目录链表1. 链表的基本概念2. 无头非循环单链表实现1) 动态申请节点2) 打印链表元素3) 插入节点头插法尾插法在指定位置之前插入在指定位置之后插入4) 删除节点删除头部节点删除末尾节点删除指定位置之前的节点删除指定位置之后的节点删除指定位置的节点5) 查找元素6) 销毁…

【图像评价】无参考图像质量评价NIQE【含Matlab源码 681期】

⛄一、无参考图像质量评价NIQE简介 理论知识参考&#xff1a;通用型无参考图像质量评价算法综述 ⛄二、部分源代码 function [mu_prisparam cov_prisparam] estimatemodelparam(folderpath,… blocksizerow,blocksizecol,blockrowoverlap,blockcoloverlap,sh_th) % Input …

013 单词速记

converse adj.相反的&#xff0c;颠倒的 v.交谈 con(加强语气)vers&#xff08;转反转&#xff09;e->反转 n.conversation 谈话&#xff0c;对话 adv.conversely 相反的 controversy n.争端 contro(counter) 相反 vers 转 lead to ~ 导致争端 contraversial 有争议…

MySQL-内置函数

文章目录内置函数日期函数字符串函数数学函数其他函数内置函数 日期函数 current_date();current_time();current_timestamp(); 应用&#xff1a; 创建生日表 插入数据&#xff1a; 创建评论区 采用datetime 时间戳自动填充时间 查询两分钟之内发的帖子 评论时间2min…

C语言期末集训1(大一,超基础,小猫猫大课堂配套练习)——顺序结构和分支结构的题

更新不易&#xff0c;麻烦多多点赞&#xff0c;欢迎你的提问&#xff0c;感谢你的转发&#xff0c; 最后的最后&#xff0c;关注我&#xff0c;关注我&#xff0c;关注我&#xff0c;你会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我…

安科瑞配电室环境监控系统解决方案-Susie 周

1、概述 配电室综合监控系统包括智能监控系统屏、通讯管理机、UPS电源、视频监控子系统&#xff08;云台球机、枪机&#xff09;、环境监测子系统&#xff08;温度、湿度、水浸、烟感&#xff09;、控制子系统&#xff08;灯光、空调、除湿机、风机、水泵&#xff09;、门禁监…

Redis分布式锁 - 基础实现及优化

应用场景 互联网秒杀抢优惠卷接口幂等性校验 代码示例 案例1 - StringRedisTemplate基础实现 package com.wangcp.redisson;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org…

以流量为王的时代,如何获得不错的流量,泰山众筹如何脱颖而出?

由于互联网、疫情等因素的影响&#xff0c;实体业务变得越来越困难。许多实体店已经开始转向在线电子商务&#xff0c;但运营一个好的电子商务平台并不容易。没有稳定的流量和忠实的用户&#xff0c;很难达到理想的效果。那到底如何才能获得不错的“流量”呢&#xff1f;泰山众…

第十四届蓝桥杯集训——JavaC组第十三篇——for循环

第十四届蓝桥杯集训——JavaC组第十三篇——for循环 目录 第十四届蓝桥杯集训——JavaC组第十三篇——for循环 for循环(重点) 倒序迭代器 for循环死循环 for循环示例 暴力循环 等差数列求和公式 基础循环展开 循环控制语句 break结束 continue继续 for循环(重点) f…

【图像融合】多尺度奇异值分解图像融合【含Matlab源码 2040期】

⛄一、多尺度奇异值分解的偏振图像融合去雾算法简介 立足于提高传统算法的适应性&#xff0c;提高去雾图像的质量&#xff0c;本文设计了如图 2 所示的去雾算法流程。首先&#xff0c;使用基于最小二乘方法计算出更加精确的偏振信息&#xff0c;改善了以往偏振信息计算不准确的…