1. 程式人生 > >C++之函式物件/偽函式(Function Object)詳解(二)

C++之函式物件/偽函式(Function Object)詳解(二)

       除了自定義的函式物件,標準庫還為我們提供了一系列現成的函式物件, 比如常見的數學、邏輯運算等。例如:negate<type>(),plus<type>(),minus<type>(),multiplies<type>(),divides<type>(),modulus<type>(),equal_to<type>,greater<type>(),less<type>(),logical_not<type>(),logical_and<type>(),等等。

       關於函式物件的最後一個很重要的概念是“函式介面卡”。函式介面卡,本質上講也是一個函式物件。這個函式物件通過將一個或多個函式物件或者特定的資料按一定規則組合起來,以完成某些特定的功能。標準庫為我們提供了幾種函式介面卡,例如:通過bind1st和bind2nd兩個包裝函式即可返回相應的介面卡,這兩個函式各有兩個形參,分別為二元函式物件和一個數值,介面卡自動把數值賦給函式物件的第1個(bind1st)或第2個(bind2nd)引數,並返回一個一元的函式物件。例如以下語句:
  1. find_if(vec.begin(),vec.end(),bind2nd(modulus<int>(),2));  
modulus<int>()初始化一個二元的函式物件,bind2nd函式把‘2’賦予其第二個引數,返回一個一元函式物件,即判斷一個整數是否為偶數。這個語句的目的即找出容器vec中第一個偶數。
       注意:不要誤把bind1st和bind2nd當成是函式介面卡,它們僅僅是兩個普通的包裝函式模板,它們返回的才是真正的函式介面卡。對應的函式物件型別分別為binder1st和binder2nd。這兩個函式模板實現如下所示:
  1. template<typename Operation,typename T>  
  2. binder1st bind1st(const Operation &op, 
    const T &t)  
  3. {  
  4.     return binder1st<Operation>(op,typename Operation::first_argument_type(t));  
  5. }  
  1. template<typename Operation,typename T>  
  2. binder1st bind2nd(const Operation &op, const T &t)  
  3. {  
  4.     return binder2nd<Operation>(op,typename Operation::second_argument_type(t));  
  5. }  
先不管具體細節,後面馬上會說到。我們只關注函式的實現,很容易看出返回的分別是類binder1st<Operation>和binder2nd<Operation>的物件,即我們所說的函式介面卡。
       除了這兩個函式介面卡,標準庫中還定義了兩個"取反器",分別可以通過呼叫輔助函式not1和not2來獲得,即unary_negate<type>和binary_negate<type>。此外,包裝函式模板mem_func_ref返回的介面卡用於自動呼叫物件的成員函式,具體細節及其他介面卡可以到C++參考網站檢視。
       函式介面卡是C++中一個強有力的工具,它允許我們針對多個函式物件按照需要進行任意組合,來產生意義豐富的表示式。這種方法被稱為"function composition"。比如對於函式f(x),g(x),h(x,y),對他們進行組合,可以生成f(g(x)),g(f(x)),h(f(x),g(x))等複雜的函式。這裡f,g分別對應了一元函式物件,h對應了二元函式物件,組合函式可以認為是函式介面卡。
       不幸的是,標準庫並沒有給我們提供太多的介面卡。因此,想要很好地利用它,還需要我們自己來定義。我們已經知道,自定義函式物件是件很容易的事情,但是想要讓函式物件能在介面卡中進行組合,則需要額外的一些工作。為了方便我們建立能在介面卡中運用的函式物件,標準庫定義了兩個有用的結構,分別為:
  1. template <class Arg, class Result>  
  2. struct unary_function  
  3. {  
  4.     typedef Arg argument_type;  
  5.     typedef Result result_type;  
  6. };  
  1. template <class Arg1, class Arg2, class Result>  
  2. struct binary_function  
  3. {  
  4.     typedef Arg1 first_argument_type;  
  5.     typedef Arg2 second_argument_type;  
  6.     typedef Result result_type;  
  7. };  
這兩個結構定義了幾個型別成員,來表示函式物件引數和返回值的型別。其中unary_function代表一元函式物件,binary_function代表二元函式物件。通過讓我們自定義的函式物件繼承相應的結構,即可在函式介面卡中使用。標準庫的函式物件正是這樣設計的,我們拿介面卡binder1st作為例子來說明下,類binder1st如下:
  1. template<class Operation>  
  2. class binder1st: public unary_function<typename Operation::second_argument_type, typename Operation::return_type>  
  3. {  
  4. public:  
  5.     binder1st(const Operation &op, consttypename Operation::first_argument_type &arg): operation(op),arg_1st(arg) {}  
  6.     typename Operation::return_type operator() (consttypename Operation::second_argument_type &arg_2nd) const
  7.     {  
  8.         return operation(arg_1st,arg_2nd);  
  9.     }  
  10. private:  
  11.     Operation operation;  
  12.     typename Operation::first_argument_type arg_1st;  
  13. };  
介面卡通過接受一個二元的函式物件(Operation)和一個該二元函式物件第一個引數型別的值(arg)呼叫建構函式。在其過載的"()"操作符函式中,只接受一個引數,對應其基類unary_function的argument_type,返回型別為unary_function的return_type。類定義中頻繁用到了typename Operation::first_argument、typename Operation::second_argument_type及typename Operation::result_type,充分說明了對Operation的要求,只要Operation繼承了binary_function即可。我們以一個計算pow(x,y)的函式物件為例子演示一下:
  1. template<typename T1, typename T2>  
  2. class Pow: public binary_function<T1,T2,T1>  
  3. {  
  4. public:  
  5.     T1 operator() (T1 base, T2 exp) const
  6.     {  
  7.         return std::pow(base,exp);  
  8.     }  
  9. };  
比如我們需要一個計算任意數字立方的函式物件,即可通過bind2nd(Pow<float,int>(),3)來得到。
       最後,我們通過自定義一個函式介面卡來結束本文。要實現的功能為: h(f(x),g(x))。首先這是個一元的函式物件,只接受一個引數x,它組合了三個函式物件H,F,G。可以這樣來實現:
  1. template<typename H,typename F,typename G>  
  2. class MyAdapter: unary_function<typename F::argument_type, typename H::return_type>  
  3. {  
  4. public:  
  5.     MyAdapter(const H &h, const F &f, const G &g):m_h(h),m_f(f),m_g(g) {}  
  6.     typename H::return_type operator() (consttypename F::argument_type &x) const
  7.     {  
  8.         return m_h(m_f(x),m_g(x));  
  9.     }  
  10. private:  
  11.     H m_h;  
  12.     F m_f;  
  13.     G m_g;  
  14. };