一直以来对于C++的使用基本上都是C with class,对于各种尖括号的模板都是敬而远之,最近忽然觉得该好好看看模板了。于是就有了这篇blog。

本文以一个查找问题为例来说明模板仿函数。

在C中,要实现一个通用的find函数(族)不大容易,有下面几种方案:

1,多个函数:

int find_int(const int List[],const int nLen,const int Target)
{
if (!nList || nLen <= )
{
return -;
}
int nIndex = -;
for (int i=;i<nLen;i++)
{
if Target == List[i])
{
nIndex = i;
break;
}
}
return nIndex;
}
int find_float(const float List[],const int nLen,const float Target);
int find_mystruct(const mystruct List[],const int nLen,const mystruct Target);

2,方法一不符合复用原则,另外一种方法是传入一个函数指针:

typedef bool (PFN_EQUALS)(const void* pVal_1,const void* pVal_2);
int find(const void* List,const int nTypeSize,const int nLen,const void* Target,const PFN_EQUALS equals)
{
if (!nList || nLen <= )
{
return -;
}
int nIndex = -;
for (int i=;i<nLen;i++)
{
if (equals(Target,(char*)List+i*nTypeSize))
{
nIndex = i;
break;
}
}
return nIndex;
} //do not check pointer : for performance
bool equals_int(const void* pVal_1,const void* pVal_2)
{
return *(int*)pVal_1 == *(int*)pVal_2;
}
bool equals_float(const void* pVal_1,const void* pVal_2);
bool equals_mystruct(const void* pVal_1,const void* pVal_2);

3,方法二同样需要写很多个equals函数,这些equals函数实际上可以合并用memcmp解决,所以有方法三:

typedef bool (PFN_EQUALS)(const void* pVal_1,const void* pVal_2,const size_t nTypeSize);
int find(const void* List,const int nTypeSize,const int nLen,const void* Target,const PFN_EQUALS equals)
{
if (!nList || nLen <= )
{
return -;
}
int nIndex = -;
for (int i=;i<nLen;i++)
{
if (equals(Target,(char*)List+i*nTypeSize,nTypeSize))
{
nIndex = i;
break;
}
}
return nIndex;
} //do not check pointer : for performance
bool equals(const void* pVal_1,const void* pVal_2,const size_t nTypeSize)
{
return ==memcmp(pVal_1,pVal_2,nSize);
}

方法三同样有一个弊病,那就是无法防止用户这样调用:find(int_list,sizeof(char),nLen,target,equals); 这或许是无意的错误,但这应该在编译就该发现,而不是运行期甚至测试之后才发现。

总结就是C中类型信息无法被传递到函数中,导致很多显而易见的错误在编译期通过。

而对于C++,该如何实现这样的一个通用函数呢?

下面是给出的一个demo,示范了如何通过模板仿函数达到目的:

#include <iostream>
#include <cassert>
using namespace std; //Equal_Functor functor prototype
template<typename T>
struct Equal_Functor
{
bool operator()(const T& v1,const T&v2)
{
return v1 == v2;
}
}; template<typename T>
int find(const T nList[],const int nLen,const T nTarget,Equal_Functor<T> equals)
{
if (!nList || nLen <= )
{
return -;
}
int nIndex = -;
for (int i=;i<nLen;i++)
{
if (equals(nTarget,nList[i]))
{
nIndex = i;
break;
}
}
return nIndex;
} struct MyStruct
{
int nVal1;
float fVal2;
double fVal3; MyStruct(int _nVal1,float _fVal2,double _fVal3)
: nVal1(_nVal1)
, fVal2(_fVal2)
, fVal3(_fVal3)
{}
}; //equals functor adapt with MyStruct
template<>
struct Equal_Functor<MyStruct>
{
bool operator()(const MyStruct& v1,const MyStruct&v2)
{
return (v1.nVal1 == v2.nVal1) && (v1.fVal2==v2.fVal2) && (v1.fVal3==v2.fVal3);
}
}; void test()
{
cout<<"test start !"<<endl;
int nTestCase = ; //int
const int myList1[] = {,,,,,,,,};
const int nLen1 = sizeof(myList1)/sizeof(int);
assert(- == find<int>(NULL,,,Equal_Functor<int>()));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl;
assert(- == find<int>(myList1,,,Equal_Functor<int>()));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl;
assert(- == find<int>(myList1,,,Equal_Functor<int>()));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl;
assert( == find<int>(myList1,nLen1,,Equal_Functor<int>()));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl; //MyStruct
const MyStruct myList3[] = {MyStruct(,5.0f,2.0f),MyStruct(,8.0f,10.0f),MyStruct(,15.0f,24.0f)};
const int nLen3 = sizeof(myList3)/sizeof(MyStruct);
assert(- == find<MyStruct>(NULL,,MyStruct(,8.0f,10.0f),Equal_Functor<MyStruct>()));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl;
assert(- == find<MyStruct>(myList3,,MyStruct(,8.0f,10.0f),Equal_Functor<MyStruct>()));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl;
assert(- == find<MyStruct>(myList3,,MyStruct(,8.0f,10.0f),Equal_Functor<MyStruct>()));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl;
assert( == find<MyStruct>(myList3,nLen3,MyStruct(,8.0f,10.0f),Equal_Functor<MyStruct>()));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl; cout<<"test done !"<<endl;
} int main(int argc,char* argv[])
{
test();
return ;
}

此处MyStruct中重载operator==函数而不是在外部对MyStruct的一个一个字段比较可能更自然一些,此处给出这种可能的写法。

为什么用仿函数而不是函数指针,原因在于c++中无法typedef一个带模板的函数,因而无法获得这样的一个函数原型传入find函数中。

----------------

另一种解决方案,将函数类型作为模板参数传进去,但此时无法保证EQFUNC的参数与T类型一致 !

template<typename T,typename EQFUNC>
int find(const T nList[],const int nLen,const T nTarget,EQFUNC equals)
{
if (!nList || nLen <= )
{
return -;
}
int nIndex = -;
for (int i=;i<nLen;i++)
{
if (equals(nTarget,nList[i]))
{
nIndex = i;
break;
}
}
return nIndex;
} template<typename T>
bool equals_function(const T& v1,const T&v2)
{
return v1==v2;
} void test()
{
cout<<"test start !"<<endl;
int nTestCase = ; //int
const int myList1[] = {,,,,,,,,};
const int nLen1 = sizeof(myList1)/sizeof(int);
assert(- == find<int>(NULL,,,equals_function<int>));
cout<<"test case "<<nTestCase++<<" OK ..."<<endl;
}

c++ 模板仿函数初探的更多相关文章

  1. Android开发之模板模式初探

    模板模式我认为在Android的开发中是最长用到的,基本是随处可见的,认识该模式,有助于我们对Android的源代码及框架有一个更深层次的认识.那什么是模板模式呢,模板模式就是定义一个基本框架,将当中 ...

  2. 从模板模式到JdbcTemplate

    模板模式初探 关于模板模式,大家可以参阅 模板方法模式深度解析(一) 原始的jdbc 关于原始的jdbc,如下: import java.sql.Connection; import java.sql ...

  3. C++中函数模板的概念和意义

    1,对泛型编程进行学习,泛型编程是实际工程开发中必用的技术,大型公司的通用 库都是采用泛型编程的技术完成的,C++ 中支持泛型编程技术,C++ 中的函数  模板和类模板就是 C++ 中泛型编程技术,本 ...

  4. C++模板之Vector与STL初探

    STL源码初步接触 STL = Standard Template Library,直译过来是:标准模板库,是惠普实验室开发的一系列软件的统称.从根本上说,STL是一些"容器"的集 ...

  5. c/c++ 函数模板初探

    函数模板初探 1,由来:有时候,函数的逻辑是一样的,只是参数的类型不同,比如下面 int Max(int a, int b){ return a > b ? a : b; } double Ma ...

  6. MVC的验证(模型注解和非侵入式脚本的结合使用) .Net中初探Redis .net通过代码发送邮件 Log4net (Log for .net) 使用GDI技术创建ASP.NET验证码 Razor模板引擎 (RazorEngine) .Net程序员应该掌握的正则表达式

    MVC的验证(模型注解和非侵入式脚本的结合使用)   @HtmlHrlper方式创建的标签,会自动生成一些属性,其中一些属性就是关于验证 如图示例: 模型注解 通过模型注解后,MVC的验证,包括前台客 ...

  7. doT js 模板引擎【初探】要优雅不要污

    js中拼接html,总是感觉不够优雅,本着要优雅不要污,决定尝试js模板引擎. JavaScript 模板引擎 JavaScript 模板引擎作为数据与界面分离工作中最重要一环,越来越受开发者关注. ...

  8. c++,模板函数的定义和使用【初探】

    // demo.cpp : // 模版函数的定义和使用: // 1.模板支持返回参数类型为模板参数. // template <typename RET_T , typename IN1_T , ...

  9. c/c++ 类模板初探

    类模板 1,模板类里的函数都是模板函数 2,模板类里的函数,在类外面实现的时候,要用模板函数(方法:push_back)的方式实现,在类内部实现时,不需要用模板函数(方法:show)方式实现. 3,用 ...

随机推荐

  1. u-boot修改出错的问题

    一.u-boot找不到nandflash 我用的开发板是fl2440的,里面没有norflash只有nandflash.我用了s3c2410的u-boot改的,所以要把内核从norflash启动改成适 ...

  2. 【web】Ubuntu上安装nodejs 4.x 5.x版本方法

    在Linux(ubuntu server)上面安装NodeJS的正确姿势 上一篇文章,我介绍了 在Windows中安装NodeJS的正确姿势,这一篇,我们继续来看一下在Linux上面安装和配置Node ...

  3. Docker默认存储路径修改

    Docker默认存储路径: # docker info...... Data loop file: /var/lib/docker/devicemapper/devicemapper/data.... ...

  4. 入侵者已经拿到了主机的管理员权限,请你列举几种留后门的方法:(windows和LINUX系统均可)

    Webshell后门 XSS后门 远控后门&rootit(windows&LINUX) SSH后门 SHIFT终端服务器后门 系统用户账号克隆 SQL数据库扩展存储型后门 SQL数据库 ...

  5. LInux——安装Apache

    在安装Apache的httpd的时候经常会遇到: configure: error: APR not found .  Please read the documentation. configure ...

  6. Docker 使用docker-compose部署项目

    原文地址:https://github.com/eacdy/spring-cloud-book/blob/master/3%20%E4%BD%BF%E7%94%A8Docker%E6%9E%84%E5 ...

  7. Android开发之用双缓冲技术绘图

    双缓冲技术主要用在画图,动画效果上,其原理就是:将资源先载入到缓冲区,然后再将缓冲区整个载入到View上面去. 双缓冲技术可以有效防止闪烁,提高显示质量. DrawView.java: package ...

  8. javascript原型的改动与重写(覆盖)区别

    每一个JavaScript函数都有prototype属性(javascript对象没有这个属性),这个属性引用了一个对象,这个对象就是原型对象.javascript同意我们改动这个原型对象. 改动有2 ...

  9. 比isConnected()更靠谱的的获取socket实时连接状态!

    看到这个标题,预计非常多人会说用socket.isConnected()或者socket.isClosed()等方法来推断即可了,但其实这些方法都是訪问socket在内存驻留的状态,当socket和s ...

  10. js将用户上传gif动图分解成多张帧图片

    js将用户上传gif动图分解成多张帧图片 写在前面 工作中遇到一个这么一个需求:这是一个多图上传的场景,如果用户上传选择多张图片,则上传后直接展示多张图片,如果上传的图片是gif动图,则需要分解这张动 ...