STL源码——function adapter函数适配器/函数配接器

来源:互联网 发布:网页压缩减肥软件 编辑:程序博客网 时间:2024/05/29 18:12

function adapters是指能够将function objects(函数对象)与另一个function objects、某个值、某个一般函数结合起来的function objects。通过function adapters可以把多个function objects结合起来,形成强大的表达式。使用时包含头文件<functional>即可。

通过function adapter的绑定、组合、修饰能力,可以创造出用户所需的各种表达式,搭配STL算法一起工作。function adapters本身也是function objects,故可以结合其他function adapters和function objects形成更复杂的表达式。例如,要找出某个序列中所有不小于12的元素个数:not1(bind2nd(less<int>(), 12)),可以与任何“接受表达式参数”的算法搭配。本语句中,对于所有小于12的元素,bind2nd(less<int>(), 12)返回true,not1再将这个结果反相,于是所有小于12的元素都会得到false。更一般而言,假如现在手头上有f()和g()这两个函数,我们希望合成一个新的运算,我们就可以通过自定义function adapter来达到效果(详见另一篇文章http://blog.csdn.net/ww32zz/article/details/47057955)。

只需要遵循一个规定:一元function object必须继承自unary_function,二元function object必须继承自binary_function,成员函数必须交由mem_fun处理,一般函数必须交由ptr_fun处理,这样就可以与其他function adapters和function objects搭配使用(称为Adaptable可适配的)。function adapter一般都是拥有一个或两个内部成员,使用待搭配使用的function objects来初始化该成员,然后在operator()重载函数中进行内部成员的函数调用,从而表现出函数的行为。

(注:对于function object来说,每一个function object必须定义自己的相应类别(参数类型,返回值类型),就像迭代器要符合STL的规定,也必须定义自己的5个相应类型一样(iterator_category、value_type、difference_type、pointer、reference),以便function adapter能够取出,以获得function object的某些信息。为了方便起见,<functional>中定义了两个classes,分别代表一元function object和二元function object,其中没有任何data members或member function,唯有一些类别定义。任何function object只需继承其中一个class,便是adaptable的。)如下:

template<class Arg, class Result>struct unary_function{typedef Arg argument_type;typedef Result result_type;};template<class Arg1, class Arg2, class Result>struct binary_funcion{typedef Arg1 first_argument_type;typedef Arg2 second_argument_type;typedef Result result_type;};

1.否定

以下function adapter用来表示某个Adaptable Predicate(谓词)的逻辑负值:

template<class Predicate>class unary_negate: public unary_function<typename Predicate::argument_type, bool>{protected:Predicate pred;   //内部成员public:explicit unary_negate(const Predicate& x) : pred(x) {}bool operator()(const typename Predicate::argument_type& x) const{return !pred(x);    //将pred的运算结果加上否定运算}};//辅助函数,由编译器进行模板参数推导,方便使用unary_negate<Pred>template <class Predicate>inline unary_negate<Predicate> not1(const Predicate& pred){return unary_negate<Predicate>(pred);}template<class Predicate>class binary_negate  : public binary_function<typename Predicate::first_argument_type,      typename Predicate::second_argument_type,     bool>{protected:Predicate pred;     //内部成员public:explicit binary_negate(const Predicate& x) : pred(x) {}bool operator()(const typename Predicate::first_argument_type& x,const typename Predicate::second_argument_type& y) const{return !pred(x, y);}};//辅助函数,由编译器进行模板参数推导,方便使用binary_negate<Pred>template<class Predicate>inline binary_negate<Predicate> not2(const Predicate& pred){return binary_negate<Predicate>(pred);}

2.绑定

以下function adapters用来将某个Adaptable Binary Function转换为Unary_Function:

template<class Operation>class binder1st  : public unary_function<typename Operation::second_argument_type,    typename Operation::result_type){protected: Operation op;     //内部成员typename Operation::first_argument_type value;    //内部成员public:binder1st(const Operation& x,  const typename Operation::first_argument_type& y)   : op(x), value(y) {}   //将表达式和第一参数记录于内部成员typename Operation::result_typeoperator()(const typename Operation::second_argument_type& y) const{return op(value, y);    //实际调用表达式,并将value绑定为第一参数}}//辅助函数,由编译器进行模板参数推导,方便使用binder1st<Op>template<class Operation, class T>inline binder1st<Operation> bind1st(const Operation& op, const T& x){typedef typename Operation::first_argument_type arg1_type;return binder1st<Operation>(op, arg1_type(x));    //先把x转型为op的第一参数类别}template<class Operation>class binder2nd  : public unary_function<typename Operation::first_argument_type,    typename Operation::result_type>{protected:Operation op;     //内部成员typename Operation::second_argument_type value;   //内部成员public:binder2nd(const Operation& x,  const typename Operation::second_argument_type& y)   : op(x), value(y) {}   //将表达式和第二参数记录于成员内部typename Operation::result_typeoperator()(const typename Operation::first_argument_type& x) const{return op(x, value);  //实际调用表达式,并将value绑定为第二参数}};template<class Operation, class T>inline binder2nd<Operation> bind2nd(const Operation& op, const T& x){typedef typename Operatioin::second_argument_type arg2_type;return binder2nd<Operation>(op, arg2_type(x));   //先把x转型为op的第二参数类别}   

注意function objects中的operator()参数应定义为const引用类型,定义为引用是为了减少复制开销,而const则是由于存在参数类型转换的问题,类型转换时产生的是临时对象(匿名对象),而参数定义为引用时,代表允许函数改变该参数,由于对临时对象的改变是无意义的,所以编译器规定应定义为const引用避免出错。

3.组合

已知两个adaptable unary_funcitons f()和g(),以下function adapter产生一个h(),使得h(x) = f(g(x));或已知一个adaptable binary_functionf(),和两个adaptable unary_functions g1(), g2(),产生一个h(x) = f(g1(x), g2(x))。

template<class Operation1, class Operation2>struct unary_compose  : public unary_function<typename Operation2::argument_type,    typename Operation1::result_type>{protected:Operation1 op1;    //内部成员Operation2 op2;    //内部成员public:unary_compose(const Operation1& x, const Operation2& y)   : op1(x), op2(y) {}       //将两个表达式记录于成员内部typename Operation1::result_typeoperator()(const typename Operation2::argument_type& x) const {return op1(op2(x));     //函数合成,表达式对外表现为接受参数x,所以为一元函数}};//辅助函数,由编译器进行模板参数推导,方便使用unary_compose<Operation1, Operation2>template<class Operation1, class Operation2>inline unary_compose<Operation1, Operation2>compose1(const Operation1& op1, const Operation2& op2){return unary_compose<Operation1, Operation2>(op1, op2);}template<class Operation1, class Operation2, class Operation3>struct binary_compose  : public unary_function<typename Operation2::argument_type,    typename Operation1::result_type>{protected:Operation1 op1;   //内部成员Operation2 op2;   //内部成员Operation3 op3;   //内部成员public:binary_compose(const Operation1& x, const Operation2& y, const Operation3& z)   : op1(x), op2(y), op3(z) {}    //将三个表达式记录于内部成员typename Operation1::result_typeoperator()(const typename Operation2::argument_type& x) const{return op1(op2(x), op3(x));   //函数合成,接受一个参数,所以为一元函数}};//辅助函数,由编译器进行模板参数推导,方便使用binary_compose<Operation1, Operation2, Operation3>template<class Operation1, class Operation2, class Operation3>inline binary_compose<Operation1, Operation2, Operation3>compose2(const Operation1& op1, const Operation2& op2, const Operation3& op3){return binary_compose<Operation1, Operation2, Operation3>(op1, op2, op3);}

3.修饰普通函数指针

这种function adapter使我们能够将一般函数指针当做function object使用。一般函数指针当做function object传给STL算法,就语言层面本来是可以的,就好像原生指针可以被当做迭代器传给STL算法一样。但如果不使用以下的function adapters先对其做一番包装,所使用的一般函数将无适配能力,也就无法和其他function objects/adapters搭配使用。实现手法和上述那些adapters差不多,就是用一个内部成员来记录对应的一元函数指针。当function object被使用时,就调用该函数指针。

template<class Arg, class Result>struct pointer_to_unary_function : public unary_function<Arg, Result>{protected:Result (*ptr)(Arg);    //内部成员,一个函数指针public:pointer_to_unary_function() {}explicit pointer_to_unary_function(Result (*x)(Arg)) : ptr(x) {}   //将函数指针记录于内部成员中//由于一般函数的参数Arg可能本身已经为引用类型,所以此处不能定义为Arg&Result operator()(Arg x) const{   return ptr(x);     //通过函数指针执行函数}};//辅助函数,由编译器进行模板参数推导,方便使用pointer_to_unary_function<Arg, Result>template<class Arg, class Result>inline pointer_to_unary_function<Arg, Result>ptr_fun(Result (*x)(Arg)){return pointer_to_unary_function<Arg, Result>(x);   }

二元函数指针类似,源码这里不贴出了。

4.修饰成员函数指针

这种function adapters使我们能够将成员函数当做function object来使用。当容器的元素是引用或者指针类型时,还可以将虚函数作为function object来实现多态。

这里介绍一下指向成员的指针:指针是指向一些内存地址的变量,既可以是数据的地址,也可以是函数的地址。成员指针亦遵从同样的概念,除了所选择的内容是在类中的成员指针。这里麻烦的是所有的指针都需要地址,但在类内部是没有地址的;选择一个类的成员意味着在类中的偏移。只有把这个偏移和具体对象的开始地址结合,才能得到实际地址。成员指针的语法要求解引用成员指针的同时选择一个对象,语法为->*(对于一个对象的指针),或者.*(对于一个对象或者引用)。比如:int ObjectClass::*pointToMember,定义一个名字为pointerToMember的成员指针,该指针可以指向ObjectClass中的任一int类型的成员;A (ObjectClass::*ptr) (B),定义一个名字为ptr的成员函数指针,该指针指向ObjectClass中的一个成员函数,该成员函数接受类型为B的参数,返回值类型为A。初始化的方法如下:ptr = &ObjectClass::f。因为仅仅提到了一个类而非那个类的对象,所以没有ObjectClass::f的确切地址,因而&ObjectClass::f仅能作为成员指针的语法被使用。这里的取地址符号&可以理解为取成员在类内的偏移量。

mem_fun和mem_fun_ref一共有8种情况,以下只取一种(通过对象的指针调用带有一个参数的non-const成员函数)进行说明:

template<class S, class T, class A>class mem_fun1_t : public binary_function<T*, A, S> {public:explicit mem_fun1_t(S (T::*pf)(A)) : f(pf) {}   //将指针记录下来S operator()(T* p, A x) const{return (p->*f)(x);   //转调用}private:S (T::*f)(A);     //内部成员,pointer to member function};//辅助函数,编译器进行模板参数推导template<class S, class T, class A>inline mem_fun1_t<S, T, A> mem_fun1(S (T::*f)(A)){return mem_fun1_t<S, T, A>(f);}

以上便是function adapter的基本用法。但是在实际应用过程中,可能会出现错误。Effective STL中最后一条中提到建议转到Boost库,就提到了STL设计者在设计的时候就犯下的一个小错误:当试图把bind2nd和mem_fun或者mem_fun_ref一起使用,来把一个对象绑定到一个成员函数的参数,而碰巧该成员函数的参数是引用类型,那么代码不能通过编译。试图把not1或not2和ptr_fun一起使用,而且该函数声明的是引用参数,会发现同样的结果。这两种情况的原因是,模板实例化过程中,大多数STL平台产生了“引用的引用”,而引用的引用在C++中不合法。

仔细看看上述bind2nd、mem_fun,not1、ptr_fun的源码就可以发现,在bind2nd和not1这些function adapter中,对operator()的重载,参数声明为const Arg&,这是因为对于可适配的function object来说,参数类别、返回值类别都是可以提取出来的typedef,而成员函数和普通函数则只能通过函数声明来获取这些信息。假设成员函数声明的参数A是引用类型,则mem_fun1_t中的第二个参数类型A也为引用类型,而在bind2nd的构造函数中:

binder2nd(const Operation& x,  const typename Operation::second_argument_type& y)   : op(x), value(y) {}

这里的second_argument_type&就会产生引用的引用。一个例子如下:

class Widget{public:……int readStream(iStream& stream);   //参数为引用……};vector<Widget> ww;……for_each(ww.begin(), ww.end(), bind2nd(mem_fun(&Widget::readStream), cin))

不过bind1st和bind2nd在C++ 11中已经被deprecated,被std::bind取代。

一个细节:上述的operator()都使用const修饰符。当函数本身不会修改类成员变量时,使用const是一种好习惯。在调用的时候const对象只能调用const成员。而非const可以调用const成员也可以调用非const ,所以在对象调用的时候并没有什么影响。

0 0