c++ 模板仿函数初探
一直以来对于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++ 模板仿函数初探的更多相关文章
- Android开发之模板模式初探
模板模式我认为在Android的开发中是最长用到的,基本是随处可见的,认识该模式,有助于我们对Android的源代码及框架有一个更深层次的认识.那什么是模板模式呢,模板模式就是定义一个基本框架,将当中 ...
- 从模板模式到JdbcTemplate
模板模式初探 关于模板模式,大家可以参阅 模板方法模式深度解析(一) 原始的jdbc 关于原始的jdbc,如下: import java.sql.Connection; import java.sql ...
- C++中函数模板的概念和意义
1,对泛型编程进行学习,泛型编程是实际工程开发中必用的技术,大型公司的通用 库都是采用泛型编程的技术完成的,C++ 中支持泛型编程技术,C++ 中的函数 模板和类模板就是 C++ 中泛型编程技术,本 ...
- C++模板之Vector与STL初探
STL源码初步接触 STL = Standard Template Library,直译过来是:标准模板库,是惠普实验室开发的一系列软件的统称.从根本上说,STL是一些"容器"的集 ...
- c/c++ 函数模板初探
函数模板初探 1,由来:有时候,函数的逻辑是一样的,只是参数的类型不同,比如下面 int Max(int a, int b){ return a > b ? a : b; } double Ma ...
- MVC的验证(模型注解和非侵入式脚本的结合使用) .Net中初探Redis .net通过代码发送邮件 Log4net (Log for .net) 使用GDI技术创建ASP.NET验证码 Razor模板引擎 (RazorEngine) .Net程序员应该掌握的正则表达式
MVC的验证(模型注解和非侵入式脚本的结合使用) @HtmlHrlper方式创建的标签,会自动生成一些属性,其中一些属性就是关于验证 如图示例: 模型注解 通过模型注解后,MVC的验证,包括前台客 ...
- doT js 模板引擎【初探】要优雅不要污
js中拼接html,总是感觉不够优雅,本着要优雅不要污,决定尝试js模板引擎. JavaScript 模板引擎 JavaScript 模板引擎作为数据与界面分离工作中最重要一环,越来越受开发者关注. ...
- c++,模板函数的定义和使用【初探】
// demo.cpp : // 模版函数的定义和使用: // 1.模板支持返回参数类型为模板参数. // template <typename RET_T , typename IN1_T , ...
- c/c++ 类模板初探
类模板 1,模板类里的函数都是模板函数 2,模板类里的函数,在类外面实现的时候,要用模板函数(方法:push_back)的方式实现,在类内部实现时,不需要用模板函数(方法:show)方式实现. 3,用 ...
随机推荐
- crontab定时任务详解
1.安装crontab:yum install crontabs 说明:/sbin/service crond start //启动服务/sbin/service crond stop //关闭服务/ ...
- vue2 router-link to
<template> <div> <nv-header></nv-header> <div class="artlist"&g ...
- 转: android 内存检测工具 LeakCanary 说明
http://www.liaohuqiu.net/cn/posts/leak-canary-read-me/ LeakCanary 中文使用说明 10 May 2015 LeakCanary Andr ...
- SQL Server 2016 Community Technology Preview 3.3
https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016 SQL Server 2016 Community Techno ...
- 除了cPickle,cjson外还有没有更高效点的序列化库了
除了cPickle,cjson外还有没有更高效点的序列化库了 http://blog.csdn.net/chen_lovelotus/article/details/7228745 msgpack最快 ...
- driver: Linux设备模型之input子系统具体解释
本节从总体上解说了输入子系统的框架结构.有助于读者从总体上认识linux的输入子系统.在陷入代码分析的过程中,通过本节的知识可以找准方向,明确原理. 本节重点: 输入子系统的框架结构 各层相应内核中的 ...
- openerp-server.conf 中配置 dbfilter 参数无效的解决办法
来自:http://shine-it.net/index.php/topic,14517.html 以前就发现过这个问题, 今天重新在群里同大家讨论了一下. 有时候可能我们希望用户不从登陆界面的账套选 ...
- C++中的INL(转)
inl 文件是内联函数的源文件. 内联函数通常在c++头文件中实现,但有的时候内联函数较多或者出于一些别的考虑(使头文件看起来更简洁等), 往往会将这部分具体定义的代码添加到INL文件中,然后在该头文 ...
- 【MyBatis学习10】高级映射之多对多查询
本文来总结一下mybatis中的多对多映射,从第8节的文章中可以看出,用户表和商品表示多对多关系,它们两的多对多是通过订单项和订单明细这两张表所关联起来的,那么这一节主要来总结一下用户表和商品表之间的 ...
- win10 rabbitMQ的安装与测试
安装 1.首先,下载并运行Erlang for Windows 安装程序 (地址:http://www.erlang.org/downloads)下载完毕并安装(注意:安装目录请选择默认目录) 2.下 ...