本章介绍的是函数对象,可能称为'高阶函数'更为适合。 它实际上是指那些可以被传入到其它函数或是从其它函数返回的一类函数。
Boost.Bind 简化了不同函数之间的绑定。 它只包含一个 boost::bind()
模板函数,定义于 boost/bind.hpp
中。它简化了由C++中的STL中 std::bind1st()
和 std::bind2nd()
模板函数所提供的机制。例如:
#include
#include
#include
#include
void add(int i, int j)
{
std::cout << i + j << std::endl;
}
int main()
{
std::vector 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()来改变函数对象的参数个数
, 第一个参数是常数值10,而第二个参数才是实际算法传入的参数。其中_1 被称为占位符(placeholder),定义于 Boost.Bind。 除了 _1,Boost.Bind 还定义了 _2 和 _3。 通过使用这些占位符,boost::bind()
可以变为一元、二元或三元的函数。
Boost.Ref 通常与 Boost.Bind 一起使用,所以我把它们挨着写。 它提供了两个函数 - boost::ref()
和 boost::cref()
- 定义于 boost/ref.hpp,表示以引用方式传递改变量。其中
模板函数 boost::cref()表示
要以引用方式传递常量对象。
#include
#include
#include
#include
void add(int i, int j, std::ostream &os)
{
os << i + j << std::endl;
}
int main()
{
std::vector 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.Function 提供了一个名为 boost::function
的类,它用来定义一个指针,指向具体的函数, 它定义于 boost/function.hpp中。
#include
#include
#include
#include
int main()
{
boost::function f = std::atoi;
std::cout << f("1609") << std::endl;
f = std::strlen;
std::cout << f("1609") << std::endl;
}
例子定义了一个指针 f,它可以指向某个接受一个类型为 const char*
的参数且返回一个类型为 int
的值的函数。 例子中先将 std::atoi()
赋值给 f,然后再将它重赋值为 std::strlen()
。注意,给定的数据类型并不需要精确匹配:虽然 std::strlen()
是以 std::size_t
作为返回类型的,但是它也可以被赋值给 f。
如果 f 未赋予一个函数而被调用,则会抛出一个 boost::bad_function_call
异常。
另外,如果 boost::function
类型的函数指针赋值为0,将会释放当前所赋的函数。 释放之后再调用它也会导致 boost::bad_function_call
异常被抛出。 要检查一个函数指针是否被赋值某个函数,可以使用 empty()
函数或 operator bool()
操作符。
通过使用 Boost.Function,类成员函数也可以被赋值给类型为 boost::function
的对象。
#include
#include
class world
{
public:
void hello(std::ostream &os)
{
os << "Hello, world!" << std::endl;
}
};
int main()
{
boost::function f = &world::hello;
world w;
f(&w, boost::ref(std::cout));
system("pause");
}
在调用这样的一个函数时,传入的第一个参数表示了该函数被调用的那个特定对象。 因此,在模板定义中的左括号后的第一个参数必须是该特定类的指针。 接下来的参数才是表示相应的成员函数的参数。 boost::ref()
,它提供了一个方便的机制向 Boost.Function 传递引用。
匿名函数 - 又称为 lambda 函数,定义在
#include
#include
#include
#include
int main()
{
std::vector v;
v.push_back(1);
v.push_back(3);
v.push_back(2);
std::for_each(v.begin(), v.end(), std::cout << boost::lambda::_1 << "\n");
}
与 Boost.Bind 相类似,Boost.Lambda 也定义了三个占位符,名为 _1, _2 和 _3。 但与 Boost.Bind 不同的是,这些占位符是定义在单独的名字空间的。 因此,该例中的第一个占位符是通过 boost::lambda::_1 来引用的。
虽然 Boost.Lambda 非常强大,但也有一些缺点。 要在以上例子中插入换行的话,必须用 "\n" 来替代 std::endl
才能成功编译。 因为一元 std::endl
模板函数所要求的类型不同于 lambda 函数 std::cout << boost::lambda::_1
的函数,所以在此不能使用它。
头文件 boost/lambda/if.hpp
定义了几个结构,允许在 lambda 函数内部使用 if
语句。 最基本的结构是 boost::lambda::if_then()
模板函数,它要求两个参数:第一个参数对条件求值 - 如果为真,则执行第二个参数。 如例中所示,每个参数本身都可以是 lambda 函数。
除了 boost::lambda::if_then()
, Boost.Lambda 还提供了 boost::lambda::if_then_else()
和 boost::lambda::if_then_else_return()
模板函数 - 它们都要求三个参数。 另外还提供了用于实现循环、转型操作符,甚至是 throw
- 允许 lambda 函数抛出异常 - 的模板函数。例如:
#include
#include
#include
#include
#include
int main()
{
std::vector v;
v.push_back(1);
v.push_back(3);
v.push_back(2);
std::for_each(v.begin(), v.end(),
boost::lambda::if_then(boost::lambda::_1 > 1,
std::cout << boost::lambda::_1 << "\n"));
}