void add(int i, int j) { std::cout <
由于std::for_each()要求的是仅接受一个参数的函数,所以不能直接传入add()函数,必须要修改源码。
#include #include #include #include class add : public std::binary_function { public: void operator()(int i, int j) const { std::cout < v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), std::bind1st(add(), 10)); }
以上程序将值10加至容器v的每个元素之上,并使用标准输出流显示结果。源代码必须作出大幅的修改,以实现此功能:add()函数已被转换为一个派生自std::binary_function的函数对象。
Boost.Bind简化了不同函数之间的绑定。它只包含一个boost::bind()模板函数,定义于boost/bind.hpp中。使用这个函数,可以如下实现以上例子:
#include #include #include #include void add(int i, int j) { std::cout < v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), boost::bind(add, 10, _1)); }
象add这样的函数不再需要为了要用于std::for_each而转换为函数对象。使用 boost::bind,该函数可以忽略其第一个参数而使用。因为add函数要求两个参数,两个参数都必须传递给boost::bind。其中第一个参数是常数值10,而第二个参数则是一个比较奇怪的_1。
_1 被称为占位符(placeholder),定义于Boost.Bind。除了_1,Boost.Bind还定义了_2 和_3。通过使用这些占位符,boost::bind可以变为一元、二元或三元的函数。对于_1, boost::bind变成了一个一元函数。这是必需的,因为 std::for_each正是要求一个一元函数作为其第三个参数。
当这个程序执行时,std::for_each对容器v中的第一个元素调用该一元函数。元素的值通过占位符_1 传入到一元函数中。这个占位符和常数值被进一步传递到add函数。通过使用这种机制,std::for_each只看到了由boost::bind所定义的一元函数。而boost::bind本身则只是调用了另一个函数,并将常数值或占位符作为参数传入给它。
下面这个例子通过boost::bind定义了一个二元函数,用于std::sort算法,该算法要求一个二元函数作为其第三个参数。
#include #include #include bool compare(int i, int j) { return i > j; } int main() { std::vector v; v.push_back(1); v.push_back(3); v.push_back(2); std::sort(v.begin(), v.end(), boost::bind(compare, _1, _2)); }
因为使用了两个占位符_1 和_2,所以boost::bind()定义了一个二元函数。 std::sort()算法以容器 v 的两个元素来调用该函数,并根据返回值来对容器进行排序。基于compare()函数的定义,容器将被按降序排列。
但是,由于compare()本身就是一个二元函数,所以使用boost::bind()确是多余的。
#include #include #include bool compare(int i, int j) { return i > j; } int main() { std::vector v; v.push_back(1); v.push_back(3); v.push_back(2); std::sort(v.begin(), v.end(), compare); }
不过使用boost::bind()还是有意义的。例如,如果容器要按升序排列而又不能修改 compare()函数的定义。
#include #include #include bool compare(int i, int j) { return i > j; } int main() { std::vector v; v.push_back(1); v.push_back(3); v.push_back(2); std::sort(v.begin(), v.end(), boost::bind(compare, _2, _1)); }
该例子仅改变了占位符的顺序:_2 被作为第一参数传递,而 _1 则被作为第二参数传递至 compare(),这样即可改变排序的顺序。
二、Boost.Ref
本库Boost.Ref通常与 Boost.Bind一起使用,所以我把它们挨着写。它提供了两个函数:boost::ref()和 boost::cref(),都定义于 boost/ref.hpp。
当要用于boost::bind()的函数带有至少一个引用参数时,Boost.Ref就很重要了。 由于boost::bind()会复制它的参数,所以引用必须特别处理。
#include #include #include #include void add(int i, int j, std::ostream &os) { os < v; v.push_back(1); v.push_back(3); v.push_back(2); std::for_each(v.begin(), v.end(), boost::bind(add, 10, _1, boost::ref(std::cout))); }
以上例子使用了上一节中的add()函数。 不过这一次该函数需要一个流对象的引用来打印信息。 因为传给boost::bind()的参数是以值方式传递的,所以std::cout不能直接使用,否则该函数会试图创建它的一份拷贝。
通过使用模板函数boost::ref(),象std::cout这样的流就可以被以引用方式传递,也就可以成功编译上面这个例子了。
要以引用方式传递常量对象,可以使用模板函数boost::cref()。
三、Boost.Function
为了封装函数指针,Boost.Function提供了一个名为boost::function的类。它定义于 boost/function.hpp,用法如下:
#include #include #include #include int main() { boost::function f = std::atoi; std::cout <
boost::function可以定义一个指针,指向具有特定签名的函数。以上例子定义了一个指针f,它可以指向某个接受一个类型为const char*的参数且返回一个类型为int的值的函数。定义完成后,匹配此签名的函数均可赋值给这个指针。这个例程就是先将std::atoi()赋值给f,然后再将它重赋值为std::strlen()。
注意,给定的数据类型并不需要精确匹配,虽然std::strlen()是以std::size_t作为返回类型的,但是它也可以被赋值给f。
因为f是一个函数指针,所以被赋值的函数可以通过重载的operator()()操作符来调用。取决于当前被赋值的是哪一个函数,在以上例子中将调用std::atoi()或std::strlen()。
如果f未赋予一个函数而被调用,则会抛出一个boost::bad_function_call异常。
#include #include int main() { try { boost::function f; f(""); } catch (boost::bad_function_call &ex) { std::cout <
注意,将值 0 赋给一个 boost::function 类型的函数指针,将会释放当前所赋的函数。释放之后再调用它也会导致 boost::bad_function_call 异常被抛出。要检查一个函数指针是否被赋值某个函数,可以使用 empty() 函数或 operator bool() 操作符。
通过使用 Boost.Function,类成员函数也可以被赋值给类型为 boost::function 的对象。
#include #include struct world { void hello(std::ostream &os) { os <<"Hello, world!" < f = &world::hello; world w; f(&w, boost::ref(std::cout)); }
在调用这样的一个函数时,传入的第一个参数表示了该函数被调用的那个特定对象。因此,在模板定义中的左括号后的第一个参数必须是该特定类的指针。 接下来的参数才是表示相应的成员函数的签名。
这个程序还使用了来自Boost.Ref库的boost::ref(),它提供了一个方便的机制向 Boost.Function传递引用。