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,用 ...
随机推荐
- shell脚本之检查局域网中在线的ip地址
[root@docker-node1 ]# cat ping.sh #!/bin/bash . /etc/init.d/functions for var in {1..254}; do ip=192 ...
- fedora25安装和docker-ce_清华源
docker-ce_清华源 https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/fedora/ fedora25 docker-ce版本: htt ...
- javascriptMVC框架面向对象编程
//抽象形状类 $.Class("Shape", {}, { //构造函数 init : function(edge) { this.edge = edge; }, //实例方法 ...
- javascript快速入门21--DOM总结
跨浏览器开发 市场上的浏览器种类多的不计其数,它们的解释引擎各不相同,期待所有浏览器都一致的支持JavaScript,CSS,DOM,那要等到不知什么时候,然而开发者不能干等着那天.历史上已经有不少方 ...
- [Android]Volley源代码分析(二)Cache
Cache作为Volley最为核心的一部分,Volley花了重彩来实现它.本章我们顺着Volley的源代码思路往下,来看下Volley对Cache的处理逻辑. 我们回忆一下昨天的简单代码,我们的入口是 ...
- cadvisor详解
一. cadvisor和k8s的耦合 cadvisor是一个谷歌开发的容器监控工具,它被内嵌到k8s中作为k8s的监控组件.现在将k8s中的cadvisor实现分析一下. k8s中和cadvisor的 ...
- python abstractmethod 对象比较
from functools import total_ordering from abc import ABCMeta,abstractmethod @total_ordering class Sh ...
- (五)Oracle学习笔记—— 视图
1. 视图简介 视图是虚表,没有具体物理数据,是通过实体表的一种计算映射逻辑.主要就是为了方便和数据安全. 2. 视图作用 简化数据操作:视图可以简化用户处理数据的方式. 着重于特定数据:不必要的数据 ...
- C#实现的根据日期得到今天是星期几
算法如下: 基姆拉尔森计算公式: W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7 在公式中d表示日期中的日数,m表示月份数,y表示年数.注意:在公式中有 ...
- xml文件的增删改读
最近学习了利用XmlDocument对象对xml进行增删改读操作,就写了一个小的例子记录下来,加深印象,以后忘了也可以找出来看看. xml文件: <?xml version="1.0& ...