boost::function用法详解

要开始使用 Boost.Function, 就要包含头文件 "boost/function.hpp", 或者某个带数字的版本,从 "boost/function/function0.hpp""boost/function/function10.hpp". 若是你知道你想保存在 function 中的函数的参数数量,这样作可让编译器仅包含须要的头文件。若是包含 "boost/function.hpp", 那么就会把其它的头文件也包含进去。ios

理解被存函数的最佳方法是把它想象为一个普通的函数对象,该函数对象用于封装另外一个函数(或函数对象)。这个被存的函数的最大用途是它能够被屡次调用,而无须在建立 function 时当即使用。在声明 functions 时,声明中最重要的部分是函数的签名。这部分便是告诉 function 它将保存的函数或函数对象的签名和返回类型。咱们已经看到,有两种方法来执行这个声明。这里有一个完整的程序,程序声明了一个 boost::function ,它能够保存返回 bool (或某个能够隐式转换为 bool 的类型)并接受两个参数的类函数实体,第一个参数能够转换为 int, 第二个参数能够转换为 double.程序员

#include <iostream>
#include "boost/function.hpp"

bool some_func(int i,double d) {
return i>d;
}

int main() {
boost::function<bool (int,double)> f;
f=&some_func;
f(10,1.1);
}

function f 首次建立时,它不保存任何函数。它是空的,能够在一个布尔上下文中进行测试。若是你试图调用一个没有保存任何函数或函数对象的 function ,它将抛出一个类型 bad_function_call 的异常。为了不这个问题,咱们用普通的赋值语法把一个指向 some_func 的指针赋值给 f 。这致使 f 保存了到 some_func 的指针。最后,咱们用参数10 (一个 int) 和 1.1 (一个 double)来调用 f (用函数调用操做符)。要调用一个 function, 你必须提供被存函数或函数对象所指望的准确数量的参数。安全

回调的基础

咱们先来看看在没有 Boost.Function 之前咱们如何实现一个简单的回调,而后再把代码改成使用 function, 并看看会带来什么优点。咱们从一个支持某种简单的回调形式的类开始,它能够向任何对新值关注的对象报告值的改变。这里的回调是一种传统的C风格回调,即便用普通函数。这种回调用可用于象GUI控制这样的场合,它能够通知观察者用户改变了它的值,而不须要对监听该信息的客户有任何特殊的知识。多线程

#include <iostream>
#include <vector>
#include <algorithm>
#include "boost/function.hpp"

void print_new_value(int i) {
std::cout <<
"The value has been updated and is now " << i << '/n';
}

void interested_in_the_change(int i) {
std::cout << "Ah, the value has changed./n";
}

class notifier {
typedef void (*function_type)(int);
std::vector<function_type> vec_;
int value_;
public:
void add_observer(function_type t) {
vec_.push_back(t);
}

void change_value(int i) {
value_=i;
for (std::size_t i=0;i<vec_.size();++i) {
(*vec_[i])(value_);
}
}
};

int main() {
notifier n;
n.add_observer(&print_new_value);
n.add_observer(&interested_in_the_change);

n.change_value(42);
}

这里的两个函数,print_new_valueinterested_in_the_change, 它们的函数签名都兼容于 notifier 类的要求。这些函数指针被保存在一个 vector 内,而且不管什么时候它的值被改变,这些函数都会在一个循环里被调用。调用这些函数的一种语法是:app

(*vec_[i])(value_);

值(value_)被传递给解引用的函数指针(即 vec_[i] 所返回的)。另外一种写法也是有效的,即这样:函数

vec_[i](value_);

这种写法看起来更好看些,但更为重要的是,它还能够容许你把函数指针更换为 Boost.Function 而没有改变调用的语法。如今,工做仍是正常的,可是,唉,函数对象不能用于这个 notifier 类。事实上,除了函数指针之外,别的任何东西都不能用,这的确是一种局限。可是,若是咱们使用 Boost.Function,它就能够工做。重写这个 notifier 类很是容易。工具

class notifier {
typedef boost::function<void(int)> function_type;
std::vector<function_type> vec_;
int value_;
public:
template <typename T> void add_observer(T t) {
vec_.push_back(function_type(t));
}

void change_value(int i) {
value_=i;
for (std::size_t i=0;i<vec_.size();++i) {
vec_[i](value_);
}
}
};

首先要作的事是,把 typedef 改成表明 boost::function 而不是函数指针。以前,咱们定义的是一个函数指针;如今,咱们使用泛型方法,很快就会看到它的用途。接着,咱们把成员函数 add_observer 的签名改成泛化的参数类型。咱们也能够把它改成接受一个 boost::function,但那样会要求该类的用户必须也知道 function 的使用方法[2],而不是仅仅知道这个观察者类型的要求就好了。应该注意到 add_observer 的这种变化并不该该是转向 function 的结果;不管如何代码应该能够继续工做。咱们把它改成泛型的;如今,无论是函数指针、函数对象,仍是 boost::function 实例均可以被传递给 add_observer, 而无须对已有用户代码进行任何改动。把元素加入到 vector 的代码有一些修改,如今须要建立一个 boost::function<void(int)> 实例。最后,咱们把调用这些函数的语法改成可使用函数、函数对象以及 boost::function 实例[3]。这种对不一样类型的相似函数的"东西"的扩展支持能够当即用于带状态的函数对象,它们能够实现一些用函数很难作到的事情。测试

[2] 他们应该知道 Boost.Function,但若是他们不知道呢?咱们添加到接口上的任何东西都必须及时向用户解释清楚。flex

[3] 如今咱们知道,一开始咱们就应该用这种语法。ui

class knows_the_previous_value {
int last_value_;
public:
void operator()(int i) {
static bool first_time=true;
if (first_time) {
last_value_=i;
std::cout <<
"This is the first change of value, /
so I don't know the previous one./n";
first_time=false;
return;
}
std::cout << "Previous value was " << last_value_ << '/n';
last_value_=i;
}
};

这个函数对象保存之前的值,并在值被改变时把旧值输出到 std::cout 。注意,当它第一次被调用时,它并不知道旧值。这个函数对象在函数中使用一个静态 bool 变量来检查这一点,该变量被初始化为 true. 因为函数中的静态变量是在函数第一次被调用时进行初始化的,因此它仅在第一次调用时被设为 true 。虽然也能够在普通函数中使用静态变量来提供状态,可是咱们必须知道那样不太好,并且很难作到多线程安全。所以,带状态的函数对象老是优于带静态变量的普通函数。notifier 类并不关心这是否是函数对象,只要符合要求就能够接受。如下更新的例子示范了它如何使用。

int main() {
notifier n;
n.add_observer(&print_new_value);
n.add_observer(&interested_in_the_change);
n.add_observer(knows_the_previous_value());

n.change_value(42);
std::cout << '/n';
n.change_value(30);
}

关键一点要注意的是,咱们新增的一个观察者不是函数指针,而是一个 knows_the_previous_value 函数对象的实例。运行这段程序的输出以下:

The value has been updated and is now 42
Ah, the value has changed.
This is the first change of value, so I don't know the previous one.

The value has been updated and is now 30
Ah, the value has changed.
Previous value was 42

在这里最大的优势不是放宽了对函数的要求(或者说,增长了对函数对象的支持),而是咱们可使用带状态的对象,这是很是须要的。咱们对 notifier 类所作的修改很是简单,并且用户代码不受影响。如上所示,把 Boost.Function 引入一个已有的设计中是很是容易的。

类成员函数

Boost.Function 不支持参数绑定,这在每次调用一个 function 就要调用同一个类实例的成员函数时是须要的。幸运的是,若是这个类实例被传递给 function 的话,咱们就能够直接调用它的成员函数。这个 function 的签名必须包含类的类型以及成员函数的签名。换言之,显式传入的类实例要做为隐式的第一个参数,this。这样就获得了一个在给出的对象上调用成员函数的函数对象。看一下如下这个类:

class some_class {
public:
void do_stuff(int i) const {
std::cout << "OK. Stuff is done. " << i << '/n';
}
};

成员函数 do_stuff 要从一个 boost::function 实例里被调用。要作到这一点,咱们须要 function 接受一个 some_class 实例,签名的其它部分为一个 void 返回以及一个 int 参数。对于如何把 some_class 实例传给 function,咱们有三种选择:传值,传引用,或者传址。如何要传值,代码就应该这样写[4]

[4] 不多会有理由来以传值的方式传递对象参数。

boost::function<void(some_class,int)> f;

注意,返回类型仍旧在最开始,后跟成员函数所在的类,最后是成员函数的参数类型。它就象传递一个 this 给一个函数,该函数暗地里用类实例调用一个非成员函数。要把函数 f 配置为成员函数 do_stuff, 而后调用它,咱们这样写:

f=&some_class::do_stuff;
f(some_class(),2);

若是要传引用,咱们要改一下函数的签名,并传递一个 some_class 实例。

boost::function<void(some_class&,int)> f;
f=&some_class::do_stuff;
some_class s;
f(s,1);

最后,若是要传 some_class 的指针[5],咱们就要这样写:

[5] 裸指针或智能指针皆可。

boost::function<void(some_class*,int)> f;
f=&some_class::do_stuff;
some_class s;
f(&s,3);

好了,全部这些传递"虚拟 this"实例的方法都已经在库中提供。固然,这种技术也是有限制的:你必须显式地传递类实例;而理想上,你更愿意这个实例被绑定在函数中。乍一看,这彷佛是 Boost.Function 的缺点,但有别的库能够支持参数的绑定,如 Boost.Bind 和 Boost.Lambda. 咱们将在本章稍后的地方示范这些库会给 Boost.Function 带有什么好处。

带状态的函数对象

咱们已经看到,因为支持了函数对象,就能够给回调函数增长状态。考虑这样一个类,keeping_state, 它是一个带状态的函数对象。keeping_state 的实例记录一个总和,它在每次调用操做符执行时被增长。如今,将该类的一个实例用于两个 boost::function 实例,结果有些出人意外。

#include <iostream>
#include "boost/function.hpp"

class keeping_state {
int total_;
public:
keeping_state():total_(0) {}

int operator()(int i) {
total_+=i;
return total_;
}

int total() const {
return total_;
}
};

int main() {
keeping_state ks;
boost::function<int(int)> f1;
f1=ks;

boost::function<int(int)> f2;
f2=ks;

std::cout << "The current total is " << f1(10) << '/n';
std::cout << "The current total is " << f2(10) << '/n';
std::cout << "After adding 10 two times, the total is "
<< ks.total() << '/n';
}

写完这段代码并接着执行它,程序员可能指望保存在 ks 的总和是20,但不是;事实上,总和为0。如下是这段程序的运行结果。

The current total is 10
The current total is 10
After adding 10 two times, the total is 0

缘由是每个 function 实例(f1f2)都含有一个 ks 的拷贝,这两个实例获得的总和都是10,但 ks 没有变化。这多是也可能不是你想要的,可是记住,boost::function 的缺省行为是复制它要调用的函数对象,这一点很重要。若是这致使不正确的语义,或者若是某些函数对象的复制代价过高,你就必须把函数对象包装在 boost::reference_wrapper 中,那样 boost::function 的复制就会是一个 boost::reference_wrapper 的拷贝,它刚好持有一个到原始函数对象的引用。你无须直接使用 boost::reference_wrapper ,你可使用另两个助手函数,refcref。 这两函数返回一个持有到某特定类型的引用或 const 引用的 reference_wrapper。在前例中,要得到咱们想要的语义,即便用同一个 keeping_state 实例,咱们就须要把代码修改以下:

int main() {
keeping_state ks;
boost::function<int(int)> f1;
f1=boost::ref(ks);

boost::function<int(int)> f2;
f2=boost::ref(ks);

std::cout << "The current total is " << f1(10) << '/n';
std::cout << "The current total is " << f2(10) << '/n';
std::cout << "After adding 10 two times, the total is "
<< ks.total() << '/n';
}

boost::ref 的用途是通知 boost::function,咱们想保存一个到函数对象的引用,而不是一个拷贝。运行这个程序有如下输出:

The current total is 10
The current total is 20
After adding 10 two times, the total is 20

这正是咱们想要的结果。使用 boost::refboost::cref 的不一样之处就象引用与 const 引用的差别,对于后者,你只能调用其中的常量成员函数。如下例子使用一个名为 something_else 的函数对象,它有一个 const 的调用操做符。

class something_else {
public:
void operator()() const {
std::cout << "This works with boost::cref/n";
}
};

对于这个函数对象,咱们可使用 boost::refboost::cref.

something_else s;
boost::function0<void> f1;
f1=boost::ref(s);
f1();
boost::function0<void> f2;
f2=boost::cref(s);
f2();

若是咱们改变了 something_else 的实现,使其函数为非const, 则只有 boost::ref 可使用,而 boost::cref 将致使一个编译期错误。

class something_else {
public:
void operator()() {
std::cout <<
"This works only with boost::ref, or copies/n";
}
};

something_else s;
boost::function0<void> f1;
f1=boost::ref(s); // This still works
f1();
boost::function0<void> f2;
f2=boost::cref(s); // This doesn't work;
// the function call operator is not const
f2();

若是一个 function 包含一个被 boost::reference_wrapper 所包装的函数对象,那么复制构造函数与赋值操做就会复制该引用,即 function 的拷贝将引向原先的函数对象。

int main() {
keeping_state ks;
boost::function1<int,int> f1; // 译注:原文为boost::function<int,int> f1,有误
f1=boost::ref(ks);

boost::function1<int,int> f2(f1); // 译注:原文为boost::function<int,int> f2(f1),有误
boost::function1<short,short> f3; // 译注:原文为boost::function<short,short> f3,有误
f3=f1;

std::cout << "The current total is " << f1(10) << '/n';
std::cout << "The current total is " << f2(10) << '/n';
std::cout << "The current total is " << f3(10) << '/n';
std::cout << "After adding 10 three times, the total is "
<< ks.total() << '/n';
}

这等同于使用 boost::ref 并把函数对象 ks 赋给每个 function 实例。

给回调函数增长状态,能够发挥巨大的能力,这也正是使用 Boost.Function 与使用函数对象相比具备的很是突出的优势。

与 Boost.Function 一块儿使用 Boost.Bind  

当咱们把 Boost.Function 与某个支持参数绑定的库结合起来使用时,事情变得更为有趣。Boost.Bind 为普通函数、成员函数以及成员变量提供参数绑定。这很是适合于 Boost.Function, 咱们经常须要这类绑定,因为咱们使用的类自己并非函数对象。那么,咱们用 Boost.Bind 把它们转变为函数对象,而后咱们能够用 Boost.Function 来保存它们并稍后调用。在将图形用户界面(GUIs)与如何响应用户的操做进行分离时,几乎老是要使用某种回调方法。若是这种回调机制是基于函数指针的,就很难避免对可使用回调的类型的某些限制,也就增长了界面表现与业务逻辑之间的耦合风险。经过使用 Boost.Function,咱们能够避免这些事情,而且当与某个支持参数绑定的库结合使用时,咱们能够垂手可得地把上下文提供给调用的函数。这是本库最多见的用途之一,把业务逻辑即从表示层分离出来。

如下例子包含一个艺术级的磁带录音机,定义以下:

class tape_recorder {
public:
void play() {
std::cout << "Since my baby left me.../n";
}

void stop() {
std::cout << "OK, taking a break/n";
}

void forward() {
std::cout << "whizzz/n";
}

void rewind() {
std::cout << "zzzihw/n";
}

void record(const std::string& sound) {
std::cout << "Recorded: " << sound << '/n';
}
};

这个磁带录音机能够从一个GUI进行控制,或者也可能从一个脚本客户端进行控制,或者从别的源进行控制,这意味着咱们不想把这些函数的执行与它们的实现耦合起来。创建这种分离的一个经常使用的方法是,用专门的对象负责执行命令,而让客户对命令如何执行毫无所知。这也被称为命令模式(Command pattern),而且在它很是有用。这种模式的特定实现中的一个问题是,须要为每一个命令建立单独的类。如下片段示范了它看起来是个什么样子:

class command_base {
public:
virtual bool enabled() const=0;
virtual void execute()=0;

virtual ~command_base() {}
};

class play_command : public command_base {
tape_recorder* p_;
public:
play_command(tape_recorder* p):p_(p) {}

bool enabled() const {
return true;
}

void execute() {
p_->play();
}
};

class stop_command : public command_base {
tape_recorder* p_;
public:
stop_command(tape_recorder* p):p_(p) {}

bool enabled() const {
return true;
}

void execute() {
p_->stop();
}
};

这并非一个很是吸引的方案,由于它使得代码膨胀,有许多简单的命令类,而它们只是简单地负责调用一个对象的单个成员函数。有时候,这是必需的,由于这些命令可能须要实现业务逻辑和调用函数,但一般它只是因为咱们所使用的工具备所限制而已。这些命令类能够这样使用:

int main() {
tape_recorder tr;

// 使用命令模式
command_base* pPlay=new play_command(&tr);
command_base* pStop=new stop_command(&tr);

// 在按下某个按钮时调用
pPlay->execute();
pStop->execute();

delete pPlay;
delete pStop;
}

如今,不用再建立额外的具体的命令类,若是咱们实现的命令都是调用一个返回 void 且没有参数(先暂时忽略函数 record, 它带有一个参数)的成员函数的话,咱们能够来点泛化。不用再建立一组具体的命令,咱们能够在类中保存一个指向正确成员函数的指针。这是迈向正确方向[6]的一大步,就象这样:

[6] 虽然损失了一点效率。

class tape_recorder_command : public command_base {
void (tape_recorder::*func_)();
tape_recorder* p_;
public:

tape_recorder_command(
tape_recorder* p,
void (tape_recorder::*func)()) : p_(p),func_(func) {}

bool enabled() const {
return true;
}

void execute() {
(p_->*func_)();
}
};

这个命令模式的实现要好多了,由于它不须要咱们再建立一组完成相同事情的独立的类。这里的不一样在于咱们保存了一个 tape_recorder 成员函数指针在 func_ 中,它要在构造函数中提供。命令的执行部分可能并非你要展示给你的朋友看的东西,由于成员指针操做符对于一些人来讲可能还不太熟悉。可是,这能够被看为一个低层的实现细节,因此还算好。有了这个类,咱们能够进行泛化处理,再也不须要实现单独的命令类。

int main() {
tape_recorder tr;

// 使用改进的命令模式
command_base* pPlay=
new tape_recorder_command(&tr,&tape_recorder::play);
command_base* pStop=
new tape_recorder_command(&tr,&tape_recorder::stop);

// 从一个GUI或一个脚本客户端进行调用
pPlay->execute();
pStop->execute();

delete pPlay;
delete pStop;
}

你可能尚未理解,咱们已经在开始实现一个简单的 boost::function 版本,它已经能够作到咱们想要的。不要重复发明轮子,让咱们重点关注手边的工做:分离调用与实现。如下是一个全新实现的 command 类,它更容易编写、维护以及理解。

class command {
boost::function<void()> f_;
public:
command() {}
command(boost::function<void()> f):f_(f) {}

void execute() {
if (f_) {
f_();
}
}

template <typename Func> void set_function(Func f) {
f_=f;
}

bool enabled() const {
return f_;
}
};

经过使用 Boost.Function,咱们能够当即从同时兼容函数和函数对象——包括由绑定器生成的函数对象——的灵活性之中获益。这个 command 类把函数保存在一个返回 void 且不接受参数的 boost::function 中。为了让这个类更加灵活,咱们提供了在运行期修改函数对象的方法,使用一个泛型的成员函数,set_function.

template <typename Func> void set_function(Func f) {
f_=f;
}

经过使用泛型方法,任何函数、函数对象,或者绑定器都兼容于咱们的 command 类。咱们也能够选择把 boost:: function 做为参数,并使用 function 的转型构造函数来达到一样的效果。这个 command 类很是通用,咱们能够把它用于咱们的 tape_recorder 类或者别的地方。与前面的使用一个基类与多个具体派生类(在那里咱们使用指针来实现多态的行为)的方法相比,还有一个额外的优势就是,它更容易管理生存期问题,咱们再也不须要删除命令对象,它们能够按值传递和保存。咱们在布尔上下文中使用 function f_ 来测试命令是否可用。若是函数不包含一个目标,即一个函数或函数对象,它将返回 false, 这意味着咱们不能调用它。这个测试在 execute 的实现中进行。如下是使用咱们这个新类的一个例子:

int main() {
tape_recorder tr;

command play(boost::bind(&tape_recorder::play,&tr));
command stop(boost::bind(&tape_recorder::stop,&tr));
command forward(boost::bind(&tape_recorder::stop,&tr));
command rewind(boost::bind(&tape_recorder::rewind,&tr));
command record;

// 从某些GUI控制中调用...
if (play.enabled()) {
play.execute();
}

// 从某些脚本客户端调用...
stop.execute();

// Some inspired songwriter has passed some lyrics
std::string s="What a beautiful morning...";
record.set_function(
boost::bind(&tape_recorder::record,&tr,s));
record.execute();
}

为了建立一个具体的命令,咱们使用 Boost.Bind 来建立函数对象,当经过这些对象的调用操做符进行调用时,就会调用正确的 tape_recorder 成员函数。这些函数对象是自完备的;它们无参函数对象,即它们能够直接调用,无须传入参数,这正是 boost::function<void()> 所表示的。换言之,如下代码片段建立了一个函数对象,它在配置好的 tape_recorder 实例上调用成员函数 play 。

boost::bind(&tape_recorder::play,&tr)

一般,咱们不能保存 bind 所返回的函数对象,但因为 Boost.Function 兼容于任何函数对象,因此它能够。

boost::function<void()> f(boost::bind(&tape_recorder::play,&tr));

注意,这个类也支持调用 record, 它带有一个类型为 const std::string& 的参数,这是因为成员函数 set_function. 由于这个函数对象必须是无参的,因此咱们须要绑定上下文以便 record 仍旧可以得到它的参数。固然,这是绑定器的工做。于是,在调用 record 以前,咱们建立一个包含被录音的字符串的函数对象。

std::string s="What a beautiful morning...";
record.set_function(
boost::bind(&tape_recorder::record,&tr,s));

执行这个保存在 record 的函数对象,将在 tape_recorder 实例 tr 上执行 tape_recorder::record,并传入字符串。有了 Boost.Function 和 Boost.Bind, 就能够实现解耦,让调用代码对于被调用代码一无所知。以这种方式结合使用这两个库很是有用。你已经在这个 command 类中看到了,如今咱们该清理一下了。因为 Boost.Function 的杰出功能,你所需的只是如下代码:

typedef boost::function<void()> command;

与 Boost.Function 一块儿使用 Boost.Lambda

与 Boost.Function 兼容于由 Boost.Bind 建立的函数对象同样,它也支持由 Boost.Lambda 建立的函数对象。你用 Lambda 库建立的任何函数对象都兼容于相应的 boost::function. 咱们在前一节已经讨论了基于绑定的一些内容,使用 Boost.Lambda 的主要不一样之处是它能作得更多。咱们能够轻易地建立一些小的、无名的函数,并把它们保存在 boost::function 实例中以用于后续的调用。咱们已经在前一章中讨论了 lambda 表达式,在那一章的全部例子中所建立的函数对象均可以保存在一个 function 实例中。function 与建立函数对象的库的结合使用会很是强大。

代价的考虑

有一句谚语说,世界上没有免费的午饭,对于 Boost.Function 来讲也是如此。与使用函数指针相比,使用 Boost.Function 也有一些缺点,特别是对象大小的增长。显然,一个函数指针只占用一个函数指针的空间大小(这固然了!),而一个 boost::function实例占的空间有三倍大。若是须要大量的回调函数,这可能会成为一个问题。函数指针在调用时的效率也稍高一些,由于函数指针是被直接调用的,而 Boost.Function 可能须要使用两次函数指针的调用。最后,可能在某些须要与C库保持后向兼容的情形下,只能使用函数指针。

虽然 Boost.Function 可能存在这些缺点,可是一般它们都不是什么实际问题。额外增长的大小很是小,并且(可能存在的)额外的函数指针调用所带来的代价与真正执行目标函数所花费的时间相比一般都是很是小的。要求使用函数而不能使用 Boost.Function 的情形很是罕见。使用这个库所带来的巨大优势及灵活性显然超出这些代价。

幕后的细节

至少了解一下这个库如何工做的基础知识是很是值得的。咱们来看一下保存并调用一个函数指针、一个成员函数指针和一个函数对象这三种情形。这三种情形是不一样的。要真正看到 Boost.Function 如何工做,只有看源代码——不过咱们的作法有些不一样,咱们试着搞清楚这些不一样的版本究竟在处理方法上有些什么不一样。咱们也有一个不一样要求的类,即当调用一个成员函数时,必须传递一个实例的指针给 function1 (这是咱们的类的名字)的构造函数。function1 支持只有一个参数的函数。与 Boost.Function 相比一个较为宽松的投条件是,即便是对于成员函数,也只须要提供返回类型和参数类型。这个要求的直接结果就是,构造函数必须被传入一个类的实例用于成员函数的调用(类型能够自动推断)。

咱们将要采用的方法是,建立一个泛型基类,它声明了一个虚拟的调用操做符函数;而后,从这个基类派生三个类,分别支持三种不一样形式的函数调用。这些类负责全部的工做,而另外一个类,function1, 依据其构造函数的参数来决定实例化哪个具体类。如下是调用器的基类,invoker_base.

template <typename R, typename Arg> class invoker_base {
public:
virtual R operator()(Arg arg)=0;
};

接着,咱们开始定义 function_ptr_invoker, 它是一个具体调用器,公有派生自 invoker_base. 它的目的是调用普通函数。这个类也接受两个类型,即返回类型和参数类型,它们被用于构造函数,构造函数接受一个函数指针做为参数。

template <typename R, typename Arg> class function_ptr_invoker 
: public invoker_base<R,Arg> {
R (*func_)(Arg);
public:
function_ptr_invoker(R (*func)(Arg)):func_(func) {}

R operator()(Arg arg) {
return (func_)(arg);
}
};

这个类模板可用于调用任意一个接受一个参数的普通函数。调用操做符简单地以给定的参数调用保存在 func_ 中的函数。请注意(的确有些奇怪)声明一个保存函数指针的变量的那行代码。

R (*func_)(Arg);

你也能够用一个 typedef 来让它好读一些。

typedef R (*FunctionT)(Arg);
FunctionT func_;

接着,咱们须要一个能够处理成员函数调用的类模板。记住,它要求在构造时给出一个类实例的指针,这一点与 Boost.Function 的作法不同。这样能够节省咱们的打字,由于是编译器而不是程序员来推导这个类。

template <typename R, typename Arg, typename T> 
class member_ptr_invoker :
public invoker_base<R,Arg> {
R (T::*func_)(Arg);
T* t_;
public:
member_ptr_invoker(R (T::*func)(Arg),T* t)
:func_(func),t_(t) {}

R operator()(Arg arg) {
return (t_->*func_)(arg);
}
};

这个类模板与普通函数指针的那个版本很类似。它与前一个版本的不一样在于,构造函数保存了一个成员函数指针与一个对象指针,而调用操做符则在该对象(t_)上调用该成员函数(func_)。

最后,咱们须要一个兼容函数对象的版本。这是全部实现中最容易的一个,至少在咱们的方法中是这样。经过使用单个模板参数,咱们只代表类型 T 必须是一个真正的函数对象,由于咱们想要调用它。说得够多了。

template <typename R, typename Arg, typename T> 
class function_object_invoker :
public invoker_base<R,Arg> {
T t_;
public:
function_object_invoker(T t):t_(t) {}

R operator()(Arg arg) {
return t_(arg);
}
};

如今咱们已经有了这些适用的积木,剩下来的就是把它们放在一块儿组成咱们的本身的 boost::function, 即 function1 类。咱们想要一种办法来发现要实例化哪个调用器。而后咱们能够把它存入一个 invoker_base 指针。这里的窃门就是,提供一些构造函数,它们有能力去检查对于给出的参数,哪一种调用器是正确的。这仅仅是重载而已,用了一点点手法,包括泛化两个构造函数。

template <typename R, typename Arg> class function1 {
invoker_base<R,Arg>* invoker_;
public:
function1(R (*func)(Arg)) :
invoker_(new function_ptr_invoker<R,Arg>(func)) {}

template <typename T> function1(R (T::*func)(Arg),T* p) :
invoker_(new member_ptr_invoker<R,Arg,T>(func,p)) {}

template <typename T> function1(T t) :
invoker_(new function_object_invoker<R,Arg,T>(t)) {}

R operator()(Arg arg) {
return (*invoker_)(arg);
}

~function1() {
delete invoker_;
}
};

如你所见,这里面最难的部分是正确地定义出推导系统以支持函数指针、类成员函数以及函数对象。不管使用何种设计来实现这类功能的库,这都是必须的。最后,给出一些例子来测试咱们这个方案。

bool some_function(const std::string& s) {
std::cout << s << " This is really neat/n";
return true;
}

class some_class {
public:
bool some_function(const std::string& s) {
std::cout << s << " This is also quite nice/n";
return true;
}
};

class some_function_object {
public:
bool operator()(const std::string& s) {
std::cout << s <<
" This should work, too, in a flexible solution/n";
return true;
}
};

咱们的 function1 类能够接受如下全部函数。

int main() {
function1<bool,const std::string&> f1(&some_function);
f1(std::string("Hello"));

some_class s;
function1<bool,const std::string&>
f2(&some_class::some_function,&s);

f2(std::string("Hello"));

function1<bool,const std::string&>
f3(boost::bind(&some_class::some_function,&s,_1));

f3(std::string("Hello"));

some_function_object fso;
function1<bool,const std::string&>
f4(fso);
f4(std::string("Hello"));
}

它也可使用象 Boost.Bind 和 Boost.Lambda 这样的 binder 库所返回的函数对象。咱们的类与 Boost.Function 中的类相比要简单多了,可是也已经足以看出建立和使用这样一个库的问题以及相关解决方法。知道一点关于一个库是如何实现的事情,对于有效使用这个库是很是有用的。

相关文章
相关标签/搜索