一直以来对于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. PHP测试用例-前言 1

    前提知识准备 在学习本课程之前,你需要准备以下知识点: 掌握一般的PHP开发技能,使用面向对象的框架开发过三个月以上 会一些JS知识 了解http协议 拥有以下知识会学得更加顺利: 掌握PHPUnit ...

  2. 关于 iOS 证书,你必须了解的知识

    收录待用,修改转载已取得腾讯云授权 最新腾讯云技术公开课直播,提问腾讯W3C代表,如何从小白成为技术专家?点击了解活动详情. 作者 |陈泽滨 编辑 | 顾乡 从事iOS开发几年,越来越发现,我们的开发 ...

  3. gensim加载word2vec训练结果(bin文件)并进行相似度实验

    # -*- coding: utf-8 -*- import gensim # 导入模型 model = gensim.models.KeyedVectors.load_word2vec_format ...

  4. Hello Ragel -- 生成状态机的神器

    Ragel 是个很 NB 的能生成状态机的编译器,而且支持一堆语言:C.C++.Object-C.C#.D.Java.Go 以及 Ruby. 原来的文本解析器是用正则表达式实现的,随着状态(if-el ...

  5. Java中的值传递与“引用传递”

    首先,Java没有 引用传递 这么一说. Java仅仅有值传递.传递的都是值,基本数据类型传递的是值.引用类型传递的是地址值. 我来理一下这当中宛如米线跟米面绞在一起了,再跟粉丝混在一起的关系. 好的 ...

  6. Wix 安装部署教程 -CustomAction的七种用法

    在WIX中,CustomAction用来在安装过程中执行自定义行为.比如注册.修改文件.触发其他可执行文件等.这一节主要是介绍一下CustomAction的7种用法. 在此之前要了解InstallEx ...

  7. 【菜鸟也疯狂UML系列】——概述

       <信息系统开发与管理>.<软件project>这两本书中都有提到过UML.想必我们对UML已经不陌生了吧,虽说非常熟悉,可是仅仅是了解而已,而今天<UML基础与应用 ...

  8. Web学习篇之---css基础知识(一)

    css基础知识(一) 1.css样式: 载入css样式有下面四种: 1).外部样式 2).内部样式 3).行内样式 4).导入样式 <link href="layout.css&quo ...

  9. SQLSTATE[HY000] [2002] Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

    SQLSTATE[HY000] [2002] Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)   这个 ...

  10. GIT基本概念和用法总结【转】

    在日常使用GIT过程中,经常会出错,比如无意间丢失了未提交的数据,回退版本时丢失了工作目录,等等.经过思考发现,所有这些错误都是因为对GIT中一些基本的概念模糊而导致,因为对一些基本概念不清晰,导致对 ...