boost::function的简单实现
前言
boost::function和boost:bind是一对强大的利器。相信用过的童鞋多少有些体会。
虽然平时在用boost::function,但是用的时候心中总会一些不安,因为不知道它是怎么实现的。于是,就自己琢磨着简单的实现一下,搞明白基本的原理。
对于这个简单实现,有以下几个目标:
- 选取比较常见的接收2个参数的情况。
- 支持普通函数/函数指针、成员函数指针。
- 兼容函数对象、函数适配器/boost::bind。
实现
首先,定义一个基类:
template<typename R, typename T1, typename T2>
class base
{
public:
virtual ~base()
{
} virtual R operator()(T1, T2) = 0;
};
然后再实现一个普通函数/函数指针的版本:
template<typename R, typename T1, typename T2>
class func : public base<R, T1, T2>
{
public:
func(R (*ptr)(T1, T2))
: ptr_(ptr)
{
} virtual R operator()(T1 a, T2 b)
{
return ptr_(a, b);
} private:
R (*ptr_)(T1, T2);
};
接着,实现支持成员函数指针的版本:
template<typename R, typename Class, typename T>
class member : public base<R, Class, T>
{
}; template<typename R, typename Class, typename T>
class member<R, Class*, T> : public base<R, Class*, T>
{
public:
member(R (Class::*ptr)(T))
: ptr_(ptr)
{
} virtual R operator()(Class* obj, T a)
{
return (obj->*ptr_)(a);
} private:
R (Class::*ptr_)(T);
};
可能有的童鞋要问,为什么这里要有一个空的member类呢?这个问题放到下面解释。
自然的轮到最后一个种情况,函数对象/boost::bind类型的了:
template<typename T, typename R, typename T1, typename T2>
class functor : public base<R, T1, T2>
{
public:
functor(const T& obj)
: obj_(obj)
{
} virtual R operator()(T1 a, T2 b)
{
return obj_(a, b);
} private:
T obj_;
};
最后,就是可用的function类了,实现如下:
template<typename T>
class function
{
}; template<typename R, typename T1, typename T2>
class function<R (T1, T2)>
{
public:
template<typename Class, typename _R, typename _T2>
function(_R (Class::*ptr)(_T2))
: ptr_(new member<R, T1, T2>(ptr))
{
} template<typename _R, typename _T1, typename _T2>
function(_R (*ptr)(_T1, _T2))
: ptr_(new func<R, T1, T2>(ptr))
{
} template<typename T>
function(const T& obj)
: ptr_(new functor<T, R, T1, T2>(obj))
{
} ~function()
{
delete ptr_;
} virtual R operator()(T1 a, T2 b)
{
return ptr_->operator()(a, b);
} private:
base<R, T1, T2>* ptr_;
};
大家可能注意到了,和前面的member类一样,function也有一个空的类,那么这些有什么用呢?
这么做的原因,主要是利用模板偏特化来进行类型萃取,正常的function声明的时候,比如function<int (int, int)>而不是func<int, int, int>。所以用模板的偏特化的版本
template<typename R, typename T1, typename T2>
class function<R (T1, T2)>
就可以把int (int, int)萃取为R = int,T1 = int,T2 = int了。
同理,对于member类,由于一般我们将成员函数指针绑定到function的时候,比如int function(Type*, int),其中Type是成员函数所属类。也就是说在function中的成员ptr_的类型是base<int, Type*, int>,那么在function的构造函数中构造的member类的类型就是member<int, Type*, int>,也就是Class = Type*,但是我们需要的却是Class = Type。所以这里得用偏特化萃取一下:
template<typename R, typename Class, typename T>
class member<R, Class*, T> : public base<R, Class*, T>
这样得到的Class模板形参就会被编译器决议为Type,而不是Type*了。
另外提一下,在function的3种情况的构造函数是模板成员函数,而不是普通成员函数:
template<typename Class, typename _R, typename _T2>
function(_R (Class::*ptr)(_T2))
: ptr_(new member<R, T1, T2>(ptr))
{
} template<typename _R, typename _T1, typename _T2>
function(_R (*ptr)(_T1, _T2))
: ptr_(new func<R, T1, T2>(ptr))
{
} template<typename T>
function(const T& obj)
: ptr_(new functor<T, R, T1, T2>(obj))
{
}
前2种情况,普通函数/函数指针对应的构造函数和成员函数指针对应的构造函数实现为成员模板,主要是为了兼容参数的隐式转换,例如声明一个function的类型为function<int (int, int)> foo,调用的时候却传入两个double类型,foo(1.1, 2.2), double类型隐式转换成了int类型。这样也符合boost:function本来的兼容可转换的调用物这一特性。
而第3种情况的成员模板,是为了获取传入的函数对象/boost::bind的类型,以便在存储在functor的数据成员中,这也是为什么functor类的模板参数比其他版本多了一个的原因。
然后,我们来测试一下:
int get(int a, int b)
{
std::cout << a+b << std::endl;
return 0;
} class Point
{
public:
int get(int a)
{
std::cout << "Point::get called: a = "<< a << std::endl;
return a;
}
int doit(int a, int b)
{
std::cout << "Point::doit called: a = "<< a+b << std::endl;
return a+b;
}
}; int main(int argc, char const *argv[])
{
function<int (int, int)> foo(get);
foo(10.1, 10.3); function<int (Point*, int)> bar(&Point::get);
Point point;
bar(&point, 30); function<int (int, int)> obj(boost::bind(&Point::doit, &point, _1, _2));
obj(90, 100);
}
结果为:
20
Point::get called: a = 30
Point::doit called: a = 190
可以看到,输出的内容正是所期望的结果。
参考文献
- boost中文手册. Improved Function Object Adapters 改良的函数对象适配器
(完)
boost::function的简单实现的更多相关文章
- boost::bind的简单实现
前言 在上一篇blog中简单的实现了boost::function,支持带有2个参数的函数/函数指针,函数对象,函数适配器/bind类,以及带有1个参数的成员函数指针. 本文接着来介绍如何实现一个简单 ...
- boost::bind 和 boost::function 基本用法
这是一篇介绍bind和function用法的文章,起因是近来读陈硕的文章,提到用bind和function替代继承,于是就熟悉了下bind和function的用法,都是一些网上都有的知识,记录一下,期 ...
- [转] boost::function用法详解
http://blog.csdn.net/benny5609/article/details/2324474 要开始使用 Boost.Function, 就要包含头文件 "boost/fun ...
- [置顶] 编程模仿boost::function和boost::bind
boost::function和boost::bind结合使用是非常强大的,他可以将成员函数和非成员函数绑定对一个对象上,实现了类似C#的委托机制.委托在许多时候可以替代C++里面的继承,实现对象解耦 ...
- boost function对象
本文根据boost的教程整理. 主要介绍boost function对象的用法. boost function boost function是什么 boost function是一组类和模板组合,用于 ...
- boost::function用法详解
要开始使用 Boost.Function, 就要包含头文件 "boost/function.hpp", 或者某个带数字的版本,从 "boost/function/func ...
- boost::function的用法
本片文章主要介绍boost::function的用法. boost::function 就是一个函数的包装器(function wrapper),用来定义函数对象. 1. 介绍 Boost.Func ...
- 以boost::function和boost:bind取代虚函数
转自:http://blog.csdn.net/Solstice/archive/2008/10/13/3066268.aspx 这是一篇比较情绪化的blog,中心思想是“继承就像一条贼船,上去就下不 ...
- boost::function实践——来自《Beyond the C++ Standard Library ( An Introduction to Boost )》
代码段1: #include <boost/function.hpp> #include <iostream> float mul_ints(int x, int y) { r ...
随机推荐
- [nginx] OpenResty 学习手册
OpenResty Installation Find tar.gz : https://openresty.org/cn/download.html tar -xzvf openresty-VERS ...
- DFS(5)——hdu1728逃离迷宫
一.题目回顾 题目链接:逃离迷宫 Problem Description 给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地 ...
- 福大软工1816:Alpha(3/10)
Alpha 冲刺 (3/10) 队名:第三视角 组长博客链接 本次作业链接 团队部分 团队燃尽图 工作情况汇报 张扬(组长) 过去两天完成了哪些任务: 文字/口头描述: 1.学习qqbot库: 2.实 ...
- WIN8/8.1/10换系统常见的几个问题
1.如果高级选项中没有UEFI固件设置,这该怎么进入BIOS呢? 答:还有一种方法是,首先关闭快速启动,然后重启,开机画面出来之后按F1,F2,F12,delete,总有一个是对的(不确定的话可以四个 ...
- 有关于PHP的基础知识
(1) l 长字符串表示,必须放在“<<<heredoc”和 “heredoc;”之间.主要是<<<,其次是也可以不使用heredoc. l “<< ...
- phpquery中文手册
[简介] phpQuery是一个基于PHP的服务端开源项目,它可以让PHP开发人员轻松处理DOM文档内容.更有意思的是,它采用了jQuery的思想,使得可以像使用jQuery一样处理页面内容,获取想要 ...
- c++ 中反正单词用到了resize()
resize(n) 调整容器的长度大小,使其能容纳n个元素.如果n小于容器的当前的size,则删除多出来的元素.否则,添加采用值初始化的元素. 题目如下: 151. Reverse Words in ...
- koajs框架学习
目录: 概述 hello world 中间件 路由中间件koa-router body解析中间件koa-bodyparser 授之以渔 一.概述 koa 是由 Express 原班人马打造的,致力于成 ...
- 51nod 1967路径定向(欧拉回路)
题目大意:给出一个图,安排边的方向,使得入度等于出度的点数最多,并给出方案. 首先假设是个无向图,不妨认定偶点必定可以满足条件 我们还会发现,奇点的个数必定是偶数个 那么如果把奇点两两用辅助边连起来, ...
- 我的emacs配置部分
考场必记8行 (global-set-key (kbd "RET") 'newline-and-indent)//换行 (global-set-key (kbd "C-a ...