记录下boost::bind的一个例子

Boost.Bind 为函数和函数对象提供了一致的语法,对于值语义和指针语义也一样。我们将从一些简单的例子开始,处理一些简单绑定的用法,然后再转移到通过嵌套绑定进行函数组合。弄明白如何使用 bind 的关键是,占位符的概念。占位符用于表示提供给结果函数对象的参数,Boost.Bind 支持最多九个参数。占位符被命名为 _1, _2, _3, _4, 直至 _9, 你要把它们放在你原先放参数的地方。作为第一个例子,我们定义一个函数,nine_arguments, 它将被一个 bind 表达式调用。

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

void nine_arguments(
  int i1,int i2,int i3,int i4,
  int i5,int i6,int i7,int i8, int i9) {
  std::cout << i1 << i2 << i3 << i4 << i5
    << i6 << i7 << i8 << i9 << '\n';
}

int main() {
  int i1=1,i2=2,i3=3,i4=4,i5=5,i6=6,i7=7,i8=8,i9=9;
  (boost::bind(&nine_arguments,_9,_2,_1,_6,_3,_8,_4,_5,_7))
    (i1,i2,i3,i4,i5,i6,i7,i8,i9);
}

在这个例子中,你创建了一个匿名临时绑定器,并立即把参数传递给它的调用操作符来调用它。如你所见,占位符的顺序是被搅乱的,这说明参数的顺序被重新安排了。注意,占位符可以在一个表达式中被多次使用。这个程序的输出如下。

921638457
这表示了占位符对应于它的数字所示位置的参数,即 _1 被第一个参数替换,_2 被第二个参数替换,等等。接下来,你将看到如何调用一个类的成员函数。

调用成员函数
我们来看一下如何用 bind 调用成员函数。我们先来做一些可以用标准库来做的事情,这样可以对比一下用 Boost.Bind 的方法。保存某种类型的元素在一个标准库容器中,一个常见的需要是对某些或全部元素调用一个成员函数。这可以用一个循环来完成,通常也正是这样做的,但还有更好的方法。考虑下面这个简单的类,status, 我们将用它来示范 Boost.Bind 的易用性和强大的功能。

class status {
  std::string name_;
  bool ok_;
public:
  status(const std::string& name):name_(name),ok_(true) {}

  void break_it() {
    ok_=false;
  }

  bool is_broken() const {
    return ok_;
  }

  void report() const {
    std::cout << name_ << " is " <<
      (ok_ ? "working nominally":"terribly broken") << '\n';
  }
};

如果我们把这个类的实例保存在一个 vector, 并且我们需要调用成员函数 report, 我们可能会象下面这样做。

std::vector<status> statuses;
statuses.push_back(status("status 1"));
statuses.push_back(status("status 2"));
statuses.push_back(status("status 3"));
statuses.push_back(status("status 4"));

statuses[1].break_it();
statuses[2].break_it();

for (std::vector<status>::iterator it=statuses.begin();
  it!=statuses.end();++it) {
  it->report();
}

这个循环正确地完成了任务,但它是冗长、低效的(由于要多次调用 statuses.end()),并且不象使用标准库算法 for_each 那样清楚地表明意图。为了用 for_each 来替换这个循环,我们需要用一个适配器来对 vector 元素调用成员函数 report 。这时,由于元素是以值的方式保存的,我们需要的是适配器 mem_fun_ref.

std::for_each(
  statuses.begin(),
  statuses.end(),
  std::mem_fun_ref(&status::report));

这是一个正确、合理的方法,它非常简洁,非常清楚这段代码是干什么的。以下是使用Boost.Bind 完成相同任务的代码。[1]

[1] 要注意的是boost::mem_fn, 它也被接纳进入Library Technical Report, 它也可以在这种没有参数的情况下使用。 mem_fn 取代了 std::mem_fun 和 std::mem_fun_ref.

std::for_each(
  statuses.begin(),
  statuses.end(),
  boost::bind(&status::report,_1));

这个版本同样的清楚、明白。这是前面所说的占位符的第一个真正的使用,我们同时告诉编译器和代码的读者,_1 用于替换这个函数所调用的绑定器的第一个实际参数。虽然这段代码节省了几个字符,但在这种情况下标准库的 mem_fun_ref 和 bind 之间并没有太大的不同,但是让我们来重用这个例子并把容器改为存储指针。

std::vector<status*> p_statuses;
p_statuses.push_back(new status("status 1"));
p_statuses.push_back(new status("status 2"));
p_statuses.push_back(new status("status 3"));
p_statuses.push_back(new status("status 4"));

p_statuses[1]->break_it();
p_statuses[2]->break_it();

我们还可以使用标准库,但不能再用 mem_fun_ref. 我们需要的是适配器 mem_fun, 它被认为有点用词不当,但它的确正确完成了需要做的工作。

std::for_each(
  p_statuses.begin(),
  p_statuses.end(),
  std::mem_fun(&status::report));

虽然这也可以工作,但语法变了,即使我们想做的事情非常相似。如果语法可以与第一个例子相同,那就更好了,所以我们所关心的是代码要做什么,而不是如何去做。使用bind, 我们就无须关心我们处理的元素是指针了(这一点已经在容器类型的声明中表明了,对于现代的库来说,这样的冗余信息是不需要的)。

std::for_each(
  p_statuses.begin(),
  p_statuses.end(),
  boost::bind(&status::report,_1));

如你所见,这与我们前一个例子完全一样,这意味着如果我们之前已经明白了 bind ,那么我们现在也清楚它。现在,我们已决定换用指针了,我们要面对另一个问题,即生存期控制。我们必须手工释放 p_statuses 中的元素,这很容易出错,也无须如此。所以,我们可能决定开始使用智能指针,并(再次)修改我们的代码。

std::vector<boost::shared_ptr<status> > s_statuses;
s_statuses.push_back(
  boost::shared_ptr<status>(new status("status 1")));
s_statuses.push_back(
  boost::shared_ptr<status>(new status("status 2")));
s_statuses.push_back(
  boost::shared_ptr<status>(new status("status 3")));
s_statuses.push_back(
  boost::shared_ptr<status>(new status("status 4")));
s_statuses[1]->break_it();
s_statuses[2]->break_it();

现在,我们要用标准库中的哪个适配器呢?mem_fun 和 mem_fun_ref 都不适用,因为智能指针没有一个名为 report 的成员函数,所以以下代码编译失败。

std::for_each(
  s_statuses.begin(),
  s_statuses.end(),
  std::mem_fun(&status::report));

不巧,标准库不能帮我们完成这个任务[2]。因此,我们不得不采用我们正想要摆脱的循环,或者使用 Boost.Bind, 它不会抱怨任何事情,而且正确地完成我们想要的。

[2] 以后将可以这样做,因为 mem_fn 和 bind 都将成为未来的标准库的一部分。

std::for_each(
  s_statuses.begin(),
  s_statuses.end(),
  boost::bind(&status::report,_1));

再一次,这段代码与前面的例子完全一样(除了容器的名字不同)。使用绑定的语法是一致的,不论是用于值语义或是指针语义,甚至是用于智能指针。有时,使用不同的语法有助于理解代码,但在这里,不是这样的,我们的任务是对容器中的元素调用成员函数,没有更多的也没有更少的事情。语法一致的价值不应被低估,因为它对于编写代码的人,以及对于日后需要维护代码的人都是有帮助的(当然,我们并不真的是在写需要维护的代码,但为了这个主题,让我们假装是在写)。

这些例子示范了一个非常基本和常见的情形,在这种情形下 Boost.Bind 尤为出色。即使标准库也提供了完成相同工作的一些基本工具,但我们还是看到 Bind 既提供了一致的语法,也增加了标准库目前缺少的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值