Function Objects(函数对象)
在了解函数对象的定义前,我们先来看一个例子。
我们考虑一个特殊的查找函数:
template <class Iterator, class Predicate>
Iterator find_if(Iterator first, Iterator last, Predicate pred)
{
while(first!=last && !pred(*first))
++first;
}
这个find_if跟一般查找某个元素的find非常相似,只不过它是查找满足pred条件的那个元素,显然它比find更加一般化。我们可以看到,这里的pred并非声明为一个函数指针(这是C的习惯做法),而是声明为一个模板参数Predicate,显然它可以包括函数指针,但是它比函数指针更加一般化。
那么,这个Predicate可以是一个对象吗?当然可以,因为function call操作符operator()可以重载,你可以把这个对象传给find_if当参数,而像这样的对象就是函数对象(当然,函数指针是最特殊的函数对象),让我们来看一个例子:
template <class Number>
struct even{
bool operator(Number x) const
{
return (x&1)==0;
}
};
那么我们查找第一个偶数可以这样写:
find_if(f,l,even());
这里even是一个class,调用了其构造函数,把它看做一个用类包装的函数好了,只不过它比一般的函数更灵活,因为它可以搭配使用任何整数类型,并不只是int,也更加有效率,因为这里的operator()是inline,不需任何函数调用。
当然,函数对象也可以像一般class一样拥有成员变量和成员函数,让我们来看一个例子,本例子的测试条件就是元素的last_name字段等于某一特定值:
template <class T>
struct last_name_is{
T value;
last_name_is(const T& val):value(val){};
bool operator(const Person& p){
return p.last_name == value;
}
};
调用函数为:
Find_if(f,l,last_name_is(“kangkang”));
很显然,任何函数对象的基本条件只是:如果f是一个函数对象,就可以使用operator()。
双参函数对象:让我们直接看例子:
template<class T>
class Add{
public:
T operator() (T& t1, T& t2)
{
return t1+t2;
}
};
cout<< Add<int>(0,8);
cout<< Add<double>(3.2,19.8);
可以看到,函数对象的行为和函数一致,即是说可以像调用函数一样来使用函数对象,如参数传递、返回值等。
函数对象(Function Objects)(2):Associated type
从前面的例子我们可以看到,函数对象有两种很重要的相关类型:参数类型,返回值类型,前面的class even就有一个参数类型T和一个返回值类型bool,这两种类型也是普通函数最重要的两种类型。
和Iterator一样,获取这些相关类型是非常重要的,同样,我们可以向Iterator一样,拥有嵌套类型。像Iterator一样,我们可以定义空的基类,这里我们定义基类unary_function和binary_function,分别对应单参和双参。
template<class Arg, class Res>
struct unary_function //单参函数对象基类
{
typedef Arg argument_type;
typedef Result result_type;
};
template<class Arg1, class Arg2, class Res>
struct binary_function //双参函数对象基类
{
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
那么修改之前的even有两种方法:
(1) 明确指出两种相关类型:
(2) 更简单的继承基类unary_function
Template <class Numble>
struct even: public unary_function<Number,bool>
{
Bool operator()(Number x) const {return (x&1)==0;}
}
但是对于函数指针,它是一种内建类型,无法定义嵌套类型,那么,我们是否可以像解决Iterator问题一样解决这个问题,在外面包一层,定义一个traits class,名为unary_function_traits和binary_function_traits。但事实上,STL并没有这样的东东,可能是函数对象还没有重要到像Iterator那样的程度,没必要为每个函数对象都提供获取相关类型的方法。