刘未鹏|C++的罗浮宫

Knowledge sharing is the best reuse

用户操作
[即时聊天] [发私信] [加为好友]
刘未鹏ID:pongba
957751次访问,排名31好友45人,关注者228
兴趣:人工智能、机器学习、知识发现,认知科学。
pongba的文章
原创 105 篇
翻译 8 篇
转载 0 篇
评论 1817 篇
刘未鹏的公告
除非特别声明,本站采用Creative Commons License许可。转载请保留作者、出处。非商业。

FeedSkyFeedBurner
或者用 鲜果 GR 抓虾 订阅。

CSDN Blog暂时不支持RSS全文输出,对此感到不便的朋友可以使用强大的greasemonkey脚本:GReader Preview Enhanced(链接),该脚本支持在GReader里面直接打开全文页面。

我经常出没于TopLanguage讨论组

我的豆瓣TwitterDelicious

《C++的罗浮宫》5年选集

——知识分享是最大的复用

下载地址:csdn资源频道|mediafire

讨论问题请到TopLanguage小组

TopLanguage


gtalk/msn(邮件请发送到gmail邮箱)

pongba@gmail.com
pp_liu@msn.com

搜索C++的罗浮宫上的内容(不要回车,点击Go)

twitters

books I've translated




这个Blog上都写了哪些东东

最近评论
kewan001:我也时不时的思考,关于如何学习,记忆等这些问题,但不曾看过什么著作,你带我看到了一个新的世界,谢过
biermando:收藏!
chenxiaoshun:恐怕是我在CSDN所看过的最好的文章。
xuxiandi:佩服啊。。。敬仰中。
YOYOZXR:偶像啊!
文章分类
收藏
相册
其它图片
文章中的图片
我的大头贴
C++
Andrei Alexandrescu
Andrew Lumsdaine
Bjarne Stroustrup
boost
C++ Standard Commitee
Doug Gregor
Hans J. Boehm
Jaakko Jarvi
Jeremy G. Siek
Matthew Wilson
newsgroups
boost.Developer
boost.User
comp.lang.c++.moderated
comp.std.c++
TopLanguage
Open Source
Ant
codeplex
Danga
Google AJAX Search API
Google Code Prettify
Google Web Toolkit
Hadoop
MS shared source initiative
notepad++
STLSoft
不认识的朋友们
fatalerror99
Glacier
realazy
SpiritEpic
TK
Yelz
丁丁虫
冰云
刘慈欣
卢昌海
吴欣安(atppp)
姬十三
林达华
浦宇平
程化
阮一峰
高远
鲍盛
机器学习/数据挖掘/信息检索/自然语言处理/认知科学/人工智能
AAAI
arXiv
Charles Kemp
Christopher Bishop
Christopher Manning
Cognitive Daily
Dan Jurafsky
David MacKay
ECML PKDD
Geoffrey Hinton
Herbert Simon
ICML
IJCAI
Jeff Hawkins
Jiawei Han
JMLR
Josh Tenenbaum
Larry Wasserman
Lucene
Marvin Minsky
MIT AI Lab
MIT Computational Cognitive Science Group
Mitchell Marcus
ML
NetLab
NIPS
Peter Norvig
Stanford AI Lab
Stanford NLP Lab
Stephen Boyd
Tom Mitchell
Trends in Cognitive Science
Vladimir Vapnik
Weka
Zhihua Zhou
其它
Gigapedia
Scientific American
Scientific American Mind
科学松鼠会
科幻世界
认识的朋友们
alai
chenyufei
dd
DreamHead
duguguiyu|Venus神庙
Googol
Joyfire
littlestone
lxwde
Matrix67
realfun
RiceBall@cnBlogs
RiceBalll
roofalison
soloist
Tinyfool
windstorm
YongSun
书剑
云风
余晟|乱象&乱想
冯大辉(Fenng)
刘新宇
刘江@图灵
史晓明
周星星
周筠@博文视点
孟岩
张志强|阅微堂
张振
徐宥|4G Spaces&Web 2.3
方舟@博文视点
曾登高
李笑来|Pure Pleasure
杨文博
熊节
王信文|地球没有好朋友
王康生
荣耀
莫华枫
蒋涛
袁泳(g9)|负暄琐话
许式伟
谢东升
谷文栋|Beyond Search
陈冀康@华章
陈怀兴
鲍志云
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 boost源码剖析之:泛型函数指针类boost::function(rev#3)收藏

新一篇: boost源码剖析之:多重回调机制signal(上) | 旧一篇: boost源码剖析之:Tuple Types(rev#2)

boost源码剖析之:泛型函数指针类boost::function(rev#3)

 

刘未鹏

C++的罗浮宫(http://blog.csdn.net/pongba)

 

Note: 并非新作,03年曾放在blog上,现在这个版本应该是修改后的最终版本。

 

前奏

如你所知,boost库是个特性完备,且具备工业强度的库,众多C++权威的参与使其达到了登峰造极的程度。尤其泛型的强大威力在其中被发挥得淋漓尽致,令人瞠目结舌。

 

然而弱水三千,我们只取一瓢饮。下面,我试图从最单纯的世界开始,一步一步带领你进入源码的世界,去探究boost::function(下文简称function)内部的精微结构。

 

通常 ,在单纯的情况下,对函数的调用简单而且直观,像这样:

 

int fun(int someVal);

  int main(){

    fun(10);

  }

 

然而你可能需要在某个时刻将函数指针保存下来,并在以后的另一个时刻调用它,像这样:

 

  int fun(int);

  typedef int (*func_handle)(int);

  int main(){

    func_handle fh=fun;

    ...  //do something

    fh(10);

  }

 

但是,如果fun形式为void fun(int)呢?如你所见,fun可能有无数种形式,如果对fun的每一个形式都typedef一个对应的func_handle,则程序员会焦头烂额,不胜其扰,代码也可能变得臃肿和丑陋不堪,甚至如果fun是仿函数呢?

 

幸运的是C++泛型可以使代码变得优雅精致,面对无数种的可能,泛型是最好的选择。

 

因此,你只是需要一个能够保存函数指针的泛型模板类(对应于Command模式),因为泛型编程有一个先天性的优势——可以借助编译器的力量在编译期根据用户提供的类型信息化身千万(实例化),所以一个泛型的类可以有无限个实例,也就是说可以保存无限多种可能类型的函数或类似函数的东西(如仿函数)。这个类(boost库中的类名为function)与函数指针相比应该有以下一些优势:

 

1. 同一个function对象应能够接受与它形式兼容的所有函数和仿函数,例如:

 

int f1(int); // 这是个函数,形式为 int(int)

short f2(double); // 这个函数形式为 short(double)

  struct functor // 这是个仿函数类,形式为int(int)

  {

    int operator()(int){}

  };

functor f3; //创建仿函数对象

 

boost::function<int(int)> func; // 能接受int(int)型的函数或仿函数

func = f1;  // 接受f1

func(10); // 调用f1(10)

func = f2;  // 也能接受short(double)型的f2

func(10); // 调用f2(10)

func = f3;  // 也能接受仿函数f3

func(10); // 调用f3(10)

 

2. function应能够和参数绑定以及其它function-construction库协同工作。例如,function应该也能够接受std::bind1st返回的仿函数。这一点其实由第一点已经有所保证。

 

3. 当接受的一个空的仿函数对象被调用的时候function应该有可预期的行为。

 

显然,第一点是我们的重点,所谓形式兼容,就是说,对于:

 

R1 (T0,T1,T2,...,TN) => FunctionType1

R2 (P0,P1,P2,...,PN) => FunctionType2

 

两种类型的函数(广义),只要满足:

 

R2能够隐式转换为R1

所有Ti都能够隐式转换为Pi (i0,1,2,...)

 

那么就说,boost::function<FunctionType1>可以接受FunctionType2类型的函数(注意,反之不行)。支持这一论断的理由是,只要Ti能够隐式转型为Pi,那么参数被转发给真实的函数调用就是安全的,并且如果R2能够隐式转型为R1,那么返回真实函数调用所返回的值就是安全的。这里安全的含义是,C++类型系统假定隐式转换不会丢失信息,或者编译器至少会给出编译警告。

 

后面你会看到,boost::function通过所谓的invoker非常巧妙地实现了这点,并且阻止了被形式不兼容的函数赋值的操作。

 

探险

先看一个function的最简单的使用:

 

int g(int); // 为了让代码简单,假设g有定义,以后的代码都会如此

function<int(int)> f(g);

f(0);

 

间奏——R(T1,T2,...)函数类型

虽然这个间奏未免早了点儿,但是为了让你以后不会带着迷惑,这是必要的。请保持耐心。

 

或许你会对模板参数int(int)感到陌生,其实它是个函数类型——函数g确切类型就是int(int),而我们通常所看到的函数指针类型int (*)(int)则是&g的类型。它们的区别与联系在于:当把g作为一个值进行拷贝的时候(例如,按值传参),其类型就会由int(int)退化为int(*)(int),即从函数类型退化为函数指针类型——因为从语义上说,函数不能被按值拷贝,但身为函数指针的地址值则是可以被拷贝的。另一方面,如果g被绑定到引用,则其类型不会退化,仍保持函数类型。例如:

 

template<class T>

void test_func_type(T ft) // 按值传递,类型退化

{

// 故意引发编译错误,在错误信息里看出ft的类型为退化后的函数指针类型

static_cast<int>(ft);

}

 

int g(int); // g确切类型为int(int)

 

test_func_type(g);  // g的类型将会退化为函数指针类型

 

int (&ref_f)(int) = g; // 由于绑定到引用,类型并不退化

 

当然,这样的代码不能通过编译,因为static_cast显然不会让一个函数指针转换为int,然而我们就是要它通不过编译,这样我们才能窥视到按值传递的参数ft的类型到底是什么,从编译错误中我们看出,ft的类型是int(*)(int),也就是说,在按值传递的过程中,g的类型退化为函数指针类型,变得和