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,用 ...
随机推荐
- 一天干掉一只Monkey计划(一)——基本光照模型及RT后处理 【转】
http://www.cnblogs.com/Zephyroal/archive/2011/10/10/2206530.html 一天干掉一只Monkey计划(一)——基本光照模型及RT后处理 1, ...
- Anaconda 介绍及安装
一.前言 Python易用,但用好却不易,其中比较头疼的就是包管理和Python不同版本的问题,特别是当你使用Windows的时候.为了解决这些问题,有不少发行版的Python,比如WinPython ...
- Node.js meitulu图片批量下载爬虫1.03版
//====================================================== // https://www.meitulu.com图片批量下载Node.js爬虫1. ...
- 算法(第四版)学习笔记之java实现可以动态调整数组大小的栈
下压(LIFO)栈:可以动态调整数组大小的实现 import java.util.Iterator; public class ResizingArrayStack<Item> imple ...
- pip安装mysql报错 ld: library not found for -lssl
ld: library not found for -lssl clang: error: linker command failed with exit code (use -v to see in ...
- 网络编程——The C10K Problem(C10K = connection 10 kilo 问题)。k 表示 kilo,即 1000
The C10K problem翻译 (C10K = connection 10 kilo 问题).k 表示 kilo,即 1000 比如:kilometer(千米), kilogram(千克). 如 ...
- iOS学习笔记23-音效与音乐
一.音频 在iOS中,音频播放从形式上能够分为音效播放和音乐播放. * 音效: * 主要指一些短音频的播放,这类音频一般不须要进行进度.循环等控制. * 在iOS中.音效我们是使用AudioToolb ...
- jmeter脚本开发-BeanShell Sampler与Debug Sampler
BeanShell支持java对象的调用,所以可直接通过BeanShell来调用java接口程序. BeanShell调用java接口例子 1.先编写两个简单的java类 创建java简单工程,并创建 ...
- CSS之Position全面认识
CSS的很多其他属性大多容易理解,比如字体,文本,背景等.有些CSS书籍也会对这些简单的属性进行大张旗鼓的介绍,而偏偏忽略了对一些难缠的属 性讲解,有避重就轻的嫌疑.CSS中主要难以理解的属性包括盒型 ...
- Android apktool反编译资源文件为空解决办法(测试天猫、淘宝等apk成功)
1 Exception in thread main brut.androlib.androlibexception multiple resources 解决办法:下载最新的apktool,在goo ...