• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 31  文章 - 128  trackbacks - 0
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(5)

            隨筆分類(38)

            隨筆檔案(31)

            收藏夾(4)

            College

            High School

            最新隨筆

            搜索

            •  

            積分與排名

            • 積分 - 55886
            • 排名 - 407

            最新評論

            • 1.?re: [yc]詳解link
            • 面試的時候面試官就問過我什么是編譯和鏈接,我說編譯就是把代碼文件生成目標文件,鏈接就是把目標文件生成可執行文件,他說不對,又問我什么是動態鏈接,還問我預編譯都做什么處理。。。都在這里找到了答案?。。?!
            • --王至乾
            • 2.?re: [yc]詳解link
            • @劉偉
              我是說博主,不是叫你啊
            • --溪流
            • 3.?re: [yc]詳解link
            • 誰是石老師,我不是哈@溪流
            • --劉偉
            • 4.?re: [yc]詳解link
            • 石老師?我是溪流~
            • --溪流
            • 5.?re: [yc]詳解link
            • 期待樓主下文啊,多謝樓主了
            • --劉偉

            閱讀排行榜

            評論排行榜

            一. 什么是Lambda
            所謂Lambda,簡單的說就是快速的小函數生成。
            在C++中,STL的很多算法都要求使用者提供一個函數對象。例如for_each函數,會要求用戶提供一個表明“行為”的函數對象。以vector<bool>為例,如果想使用for_each對其中的各元素全部賦值為true,一般需要這么一個函數對象,

             

              class  filler
             
            {
             
            public :
              
            void   operator ()( bool   & i)  const   {i  =   true ;}
             }
            ;

             

            這樣實現不但麻煩,而且不直觀。而如果使用lambda,則允許用戶使用一種直觀和見解的方式來處理這個問題。以boost.lambda為例,剛才的問題可以這么解決:

             

            for_each(v.begin(), v.end(), _1  =   true );

             

            那么下面,就讓我們來實現一個lambda庫。

             

            二. 戰前分析
            首先要說明的是,我并沒有讀過boost.lambda或其他任何lambda庫的代碼,因此如代碼有雷同,純屬巧合。
            開始實現以前,首先要分析出大致的實現手法。先讓我們來看幾段使用Lambda的代碼
             

            for_each(v.begin(), v.end(), _1  =   1 );
             
            /* --------------------------------------------- */
             vector
            < int *>  vp( 10 ); 
             transform(v.begin(), v.end(), vp.begin(), 
            & _1);  /* --------------------------------------------- */
             sort(vp.begin(), vp.end(), 
            * _1  >   * _2);
             
            /* --------------------------------------------- */
             
            int  b  =   * find_if(v.begin, v.end(), _1  >=   3   &&  _1  <   5 );
             
            /* --------------------------------------------- */
             for_each(vp.begin(), vp.end(), cout 
            <<   * _1  <<   ' \n ' );
             
            /* --------------------------------------------- */
             for_each(vp.begin(), vp.end(), cout 
            <<  constant( ' \n ' <<   * _1);

             

            看了之后,我們可以思考一些問題:
            1._1, _2是什么?
            顯然_1和_2都滿足C++對于標識符的要求,可見_1和_2都是對象。
            2._1 = 1是在做什么?
            既然_1是一個對象,那么_1的類必然重載了operator=(int)。那么operator=返回什么呢?該函數所返回的對象被傳入for_each的第3個參數,可見其返回了一個函數對象。現在整個流程就很清楚了。_1 = 1調用了operator=,其返回了一個函數對象,該函數對象能夠將參數1賦值為1。
            Ok,回答了這兩個問題之后,我們的思路就很清晰了。如果要實現operator=,那么至少要實現2個類,一個用于產生_1的對象,另一個用于代表operator=返回的函數對象。


            三. 動工
            首先實現一個能夠范型的進行賦值的函數對象類:

             

             template < typename T >
             
            class  assignment
             
            {
              T value;
             
            public :
              assignment(
            const  T &  v) : value(v) {}
              template
            < typename T2 >
              T2
            &   operator ()(T2  & rhs)  const   return  rhs  =  value; }
             }

             

            其中operator()被聲明為模版函數以支持不同類型之間的賦值。
            然后我們就可以書寫_1的類來返回assignment

             

              class  holder
             
            {
             
            public :
              template
            < typename T >
              assignment
            < T >   operator = ( const  T &  t)  const
              
            {
               
            return  assignment < T > (t);
              }

             }
            ;

             

            由于該類是一個空類,因此我們可以在其后放心大膽的寫上:

              static  holder _1;

            Ok,現在一個最簡單的lambda就完工了。你可以寫

            for_each(v.begin(), v.end(), _1  =   1 );

            而不用手動寫一個函數對象。

             

            四. 問題分析
            雖然基本上一個Lambda已經初步實現出來了,但是仔細想想,問題也是很多的。
            1, 我們現在是把_1和functor看成兩個不同的存在,會導致代碼的重復。
            2, 目前這個Lambda還無法實現如_1 = 2 = 3這樣的鏈式操作。
            3, 我們沒有設計好如何處理多個參數的functor。
            下面我們可以對這幾個問題進行分析。

            五. 問題1:一致性
            首先來看看1,合并_1和functor的最佳方法就是把_1本身也變成functor。那么_1的operator()會做什么事情呢?|
            很明顯,_1的operator()僅僅應該返回傳進來的參數本身。

            struct  holder
             
            {
              
            //
              template < typename T >
              T
            &   operator ()( const  T &  r)  const
              
            {
               
            return  (T & )r;
              }

             }
            ;

            這樣的話assignment也必須相應改動:

            template < typename Left, typename Right >
             
            class  assignment
             
            {
              Left l;
              Right r;
             
            public :
              assignment(
            const  Left &  l,  const  Right &  r) : l(l), r(r) {}
              template
            < typename T2 >
              T2
            &   operator ()(T2  & rhs)  const   return  l(rhs)  =  r; }
             }

            同時,holder的operator=也需要改動:

            template < typename T >
             assignment
            < holder, T >   operator = ( const  T &  t)  const
             
            {
              
            return  assignment < holder, T > ( * this , t);
             }

            好,這樣holder也成為了一個functor,這為我們以后添加功能節省了很多代碼。
            你可能也注意到,常數和functor地位也不平等。

            return  l(rhs)  =  r;

            在這一句中,r沒有調用operator()而l調用了。這樣以后就要不時的區分常數和functor,是不良的設計。
            那么我們仿造holder的做法實現一個常數類:

            template < typename Tp >
             
            class  constant_t
             
            {
              
            const  Tp t;
             
            public :
              constant_t(
            const  Tp &  t) : t(t) {}
              template
            < typename T >
              
            const  Tp &   operator ()( const  T &  r)  const
              
            {
               
            return  t;
              }

             }
            ;

            該functor的operator()無視參數,直接返回內部所存儲的常數。
            下面就可以修改holder的operator=了

            template < typename T >
             assignment
            < holder, constant_t < T >   >   operator = ( const  T &  t)  const
             
            {
              
            return  assignment < holder, constant_t < T >   > ( * this , constant_t < T > (t));
             }

            同時也要修改assignment的operator()

             template < typename T2 >
             T2
            &   operator ()(T2  & rhs)  const   return  l(rhs)  =  r(rhs); }

            現在代碼看起來就很一致了。

            六. 問題2:鏈式操作
            現在讓我們來看看如何處理鏈式操作。
            其實問題1已經為我們處理掉了大量的問題。如果_1,functor,常量彼此之間不統一為functor,那么鏈式操作的時候就要時刻小心一個對象是_1還是functor還是常量,會大大增加編碼的難度。
            事實上,首先要解決的是,如何知道一個functor的operator()的返回值的類型。遺憾的是,我并沒有找到非常自動的辦法,因此我們得讓functor自己來告訴我們返回值的類型。
            比較麻煩的是,operator()的返回值一般和其參數的類型相關,而operator()通常是一個模版函數,因此其返回值類型并不能用一個簡單的typedef來指定,而必須實現一個trait。
            現在我們在assignment內部聲明一個nested-struct

            template < typename T >
             
            struct  result_1
             
            {
              typedef typename 
            ref < typename Left::result_1 < T > ::result > ::reference result;
             }
            ;

            那么如果參數為T,其返回值類型就為result_1<T>::result。上面代碼的ref<T>為一個類型轉換類,作用是返回T的引用。不直接加上&符號的原因是如果T本身就是Q的引用Q&,那么Q&&是非法的。因此ref的實現即為:

            template < typename T >
             
            struct   ref
             
            {
              typedef T
            &  reference;
             }
            ;
             template
            < typename T >
             
            struct   ref < T &>
             
            {
              typedef T
            &  reference;
             }
            ;

            有了result_1之后,就可以把operator()改寫一下:

             template < typename T >
             typename result_1
            < T > ::result  operator ()( const  T &  t)  const
             
            {
              
            return  l(t)  =  r(t);
             }

            可能大家已經注意到我定義assignment的operator()的返回類型的時候,是直接將其定義為Left的operator()返回類型的引用形式,如果實際上處理的對象的operator=并不是按照常理來聲明的,那么這段代碼可能就編譯不過。這的確是一個很麻煩的事情。實際上,在gcc下,使用typeof關鍵字可以很容易的得到該類型的operator=的返回類型,就可以讓這段代碼變得更有通用性。然而為了實現可移植性,我不得不放棄這個誘人的想法。
             同理我們可以給constant_t和holder加上這個result_1。
             
            有了這個result_1,鏈式操作就簡單多了?,F在唯一要做的事情就是讓所有的functor都重載各種操作符以產生新的functor。假設我們有add和divide兩個類,那么
            _1 / 3 + 5會出現的構造方式是:
             _1 / 3調用holder的operator/ 返回一個divide的對象
             +5 調用divide的對象返回一個add對象。
            最后的布局是:
                                     Add
                                   /     \
                              Divide     5
                              /    \
                            _1      3
            似乎一切都解決了?不。
            你可以想象一下一個完整的Lambda庫,它必然能夠重載C++幾乎所有的操作符。假設其重載了10個操作符,那么至少會有10個代表這些操作符的functor類。大體上來講,每一種操作符所對應的functor都應當能夠由鏈式操作產生別的任意一種操作符所對應的functor。(例如:*_1 = 2既是由operator*的functor產生operator=的functor)??上攵@樣一共能產生10*10=100種產生方式。這是對編碼的一個大挑戰。
            如何簡化這個問題呢?我們不妨假定,任意一種操作符的functor,都能夠產生任意一種操作符的functor,這樣,每一種操作符的functor都擁有一樣的產生方案。如果某種轉換確實是不合法的(例如:A/B=C無論如何也不可能合法),那么在試圖產生新functor的時候會出現編譯錯誤。幸好C++的模版是如果不使用就不編譯的,因此這種編譯錯誤不會干擾到正常的使用,這正是我們所要的。
            OK,我們的方法呼之欲出了。既然所有的functor都具有一樣的產生方案,那么不如大家都不要實現,等到最后統一的在所有的functor里面加上這么一系列的產生代碼吧。例如,如果要添加從某functor XXX到operator=的functor的產生代碼:

             template < typename Right >
             assignment
            < XXX, typename picker_maker < Right > ::result  >   operator = ( const  
             Right
            &  rt)  const
             
            {
              
            return  assignment < XXX, typename picker_maker < Right > ::result  > ( * this , rt);
             }

            下面對該代碼的一些細節方面作一些解釋
            XXX指的是原來的functor的類型,picker_maker<T>是一個類型變換的trait,如果T是一個常量,那么他會返回constant_t<T>,否則返回T本身。
            因此如果該函數聲明在assignment的內部,那么就實現了連等,如果聲明在的dereference(解引用)的內部,就允許(*A = B)的行為發生。
            最后,如何把這些函數塞到各個functor的聲明里邊呢?當然可以用宏,但是。。。大家都知道這樣不好。
            除了宏之外還可以用的方式就是繼承。我們可以寫一個類叫做picker,該類實現了所有的如上的產生函數。然后讓所有的functor繼承自它。
            且慢,也許立刻就有人跳出來說:這樣的話那個XXX怎么寫呢?這樣不是會導致循環依賴么?這樣不是會有downcast么?
            正解,讓picker做基類確實不是一個好主意。反過來,讓picker繼承functor卻是一個不錯的方法。下面是picker的聲明:

            template < class  Action >
             
            class  picker :  public  Action
             
            {
             
            public :
              picker(
            const  Action &  act) : Action(act) {}
               
            // all the operator overloaded
             }
            ;

            Picker<T>繼承自T,唯一的作用就是給T添加上了各種操作符的重載函數。
            現在所有參與行動的functor都要套上一層picker, _1被聲明為 picker<holder>, 并且holder中所重載的操作符除了operator()之外全部被移到了picker內。而picker中的操作符重載的返回的functor也必須套上一個picker:

            template < typename Right >
             picker
            < assignment < Action, typename picker_maker < Right > ::result  >   >   operator = ( const  Right &  rt)  const
             
            {
              
            return  assignment < Action, typename picker_maker < Right > ::result  >  ( * this , rt);
             }

            Piker_maker返回的也是picker<T>,或者picker<constant_t<T> >
            使用picker還帶來一個額外的好處。之前提到picker_maker要區分functor和常量,有了picker,區分的方法就非常簡單了:凡是屬于picker<T>的都是functor,否則就是常量。

            template  < typename T >   struct  picker_maker
             
            {
              typedef picker
            < constant_t < T >   >  result;
             }
            ;
             template 
            < typename T >   struct  picker_maker < picker < T >   >
             
            {
              typedef picker
            < T >  result;
             }
            ;

            下面總的結構就有了:
             functor專心模擬操作符的行為,并實現一個result_1來告訴別人自己的返回類型。
             picker專心負責操作符之間的產生關系,由它來聯系操作符合functor。
             picker<functor>構成了實際參與操作的對象。
            至此鏈式操作完美實現。


            七. 問題3
            如何使用多參數的函數對象呢?考慮_1=_2,這個functor必須接受2個參數,因此所產生的assignment對象的operator()必須能接收2個參數。

            template < typename T1, typename T2 >
             
            ???   operator ()( const  T1 &  t1,  const  T2 &  t2)  const
             
            {
              
            return  lt(t1, t2)  =  rt(t1, t2);
             }

            很明顯,這個函數的返回類型會依賴于T1,T2,因此result_1已經無法適用,我們就只好再寫一個result_2:

             template < typename T1, typename T2 >
             
            struct  result_2
             
            {
              typedef typename 
            ref < typename Left::result_2 < T1, T2 > ::result > ::reference result;
             }
            ;

            顯然,各個functor似乎根本不理會各個參數那個是_1, 那個是_2, 那么最后是怎么選擇的呢?
            這個差事就留給了holder自己。
                    

            template < int  Order >
             
            class  holder;
             template
            <>
             
            class  holder < 1 >
             
            {
             
            public :
              template
            < typename T >
              
            struct  result_1
              
            {
               typedef T
            &  result;
              }
            ;
              template
            < typename T1, typename T2 >
              
            struct  result_2
              
            {
               typedef T1
            &  result;
              }
            ;
              template
            < typename T >
              typename result_1
            < T > ::result  operator ()( const  T &  r)  const
              
            {
               
            return  (T & )r;
              }

              template
            < typename T1, typename T2 >
              typename result_2
            < T1, T2 > ::result  operator ()( const  T1 &  r1,  const  T2 &  r2)  const
              
            {
               
            return  (T1 & )r1;
              }

             }
            ;

             template
            <>
             
            class  holder < 2 >
             
            {
             
            public :
              template
            < typename T >
              
            struct  result_1
              
            {
               typedef T
            &  result;
              }
            ;
              template
            < typename T1, typename T2 >
              
            struct  result_2
              
            {
               typedef T2
            &  result;
              }
            ;
              template
            < typename T >
              typename result_1
            < T > ::result  operator ()( const  T &  r)  const
              
            {
               
            return  (T & )r;
              }

              template
            < typename T1, typename T2 >
              typename result_2
            < T1, T2 > ::result  operator ()( const  T1 &  r1,  const  T2 &  r2)  const
              
            {
               
            return  (T2 & )r2;
              }

             }
            ;

            新的holder變成了holder<int>, holder<n>的n個參數的operator()會返回第n個參數的值。而_1,_2也相應變為picker<holder<1> >, picker<holder<2> >。
            現在讓我們來看看(_1 = _2)(i. j)是怎么調用的:
             首先 assignment::operator(int, int)被調用:

            return  l(i, j)  =  r(i, j);

             先后調用holder<1>::operator()(int, int)和holder<2>::operator()(int, int)

               return  ( int & )i;
              
            return  ( int & )j;

            最后執行i = j;
            可見,參數被正確的選擇了。

             


            八. 中期總結
            目前的結果是這樣的,為了支持一個操作符,我們需要作如下幾件事:
             1。 實現一個functor,該functor的operator()要能執行該操作符的語義
             2。 在該functor中實現result_1至result_n,其中n是支持參數的最大值。
             3。 在picker中實現一個操作符重載,返回該functor

             

             

            九. 簡化
            很明顯,要支持一個操作符所要做的工作太多了,而且在每個functor中申明result_1至result_n,可見如果n發生變化,維護的開銷極大。
            我們現在需要找到一個自動生成這種functor的方法。
            首先,我們注意到result_x的形式很統一。對于各種操作符,其返回值無非下列幾種:
             1. 返回值。如果本身為引用,就去掉引用。  
               +-*/&|^等
             2. 返回引用。
               =,各種復合賦值等
             3. 返回固定類型。
               各種邏輯/比較操作符(返回bool)
             4. 原樣返回。
               operator,
             5. 返回解引用的類型。
               operator*(單目)
             6. 返回地址。
               operator&(單目)
             7. 下表訪問返回類型。
               operator[]
             8. 如果左操作數是一個stream,返回引用,否則返回值
               operator<<和operator>>

            OK,這樣我們將返回值類型總結為以上8種,就可以將各種result_x從functor中剝離出來了。
            例如針對第一條,我們實現一個policy類:

            template < typename Left >
             
            struct  value_return
             
            {
              template
            < typename T >
              
            struct  result_1
              
            {
               typedef typename const_value
            < typename Left::template result_1 < T > ::result_type > ::value_type result_type;
              }
            ;

              template
            < typename T1, typename T2 >
              
            struct  result_2
              
            {
               typedef typename const_value
            < typename Left::template result_2 < T1, T2 > ::result_type > ::value_type result_type;
              }
            ;
             }
            ;

            其中const_value是一個將一個類型轉為其非引用形式的trait

            下面我們來剝離functor中的operator()
            首先operator里面的代碼全是下面的形式:

            return  l(t) op r(t)
             
            return  l(t1, t2) op r(t1, t2)
             
            return  op l(t)
             
            return  op l(t1, t2)
             
            return  l(t) op
             
            return  l(t1, t2) op
             
            return  l(t)[r(t)]
             
            return  l(t1, t2)[r(t1, t2)]

            很自然的,我們會想到用函數替代這種操作符行為以獲得更加一致的形式:
             單目: return f(l(t), r(t));
              return f(l(t1, t2), r(t1, t2));
             雙目: return f(l(t));
              return f(l(t1, t2));
            下面就是f的實現,以operator/為例

            struct  meta_divide
             
            {
              template 
            < typename T1, typename T2 >
              
            static  ret execute( const  T1 &  t1,  const  T2 &  t2) 
              
            {
               
            return  t1  /  t2;
              }
             
             }
            ;

            這個工作可以讓宏來做:

            #define  DECLARE_META_BIN_FUNC(op, desc, ret) struct meta_##desc{\
              template 
            < typename T1, typename T2 >  \
              
            static  ret execute( const  T1 &  t1,  const  T2 &  t2)  { return  ((T1 & )t1) op ((T2 & )t2);}  };

            以后可以直接用
             DECLARE_META_BIN_FUNC(/, divide, T1)
            來申明meta_divide。同樣還可以申明宏DECLARE_META_UNY_PRE_FUNC和DECLARE_META_UNY_POST_FUNC來產生單目前綴和后綴操作符的函數
            (ps.我本堅持該lambda實現不使用宏的,但是在這種小劑量的又很一致的代碼面前,使用宏實在是很誘人。。。)


            下面就是要把operator()和result_x拼湊起來,形成一個我們要的functor,下面是一個單目的functor的實現體

            template < typename Left, typename Right, typename Rettype, typename FuncType >
             
            class  unary_op :  public  Rettype
             
            {
                  Left l;
             
            public :
                  unary_op(
            const  Left &  l) : l(l) {}

              template
            < typename T >
                  typename Rettype::template result_1
            < T > ::result_type  operator ()( const  T &  t)  const
                  
            {
                      
            return  FuncType::execute(l(t));
                  }


                  template
            < typename T1, typename T2 >
                  typename Rettype::template result_2
            < T1, T2 > ::result_type  operator ()( const  T1 &  t1,  const  T2 &  t2)  const
                  
            {
                      
            return  FuncType::execute(l(t1, t2));
                  }

             }
            ;

            同樣還可以申明一個binary_op

            template < typename Left, typename Right, typename Rettype, typename FuncType >
             
            class  binary_op :  public  Rettype
             
            {
                  Left l;
              Right r;
             
            public :
                  binary_op(
            const  Left &  l, const  Right &  r) : l(l), r(r) {}

              template
            < typename T >
                  typename Rettype::template result_1
            < T > ::result_type  operator ()( const  T &  t)  const
                  
            {
                      
            return  FuncType::execute(l(t), r(t));
                  }


                  template
            < typename T1, typename T2 >
                  typename Rettype::template result_2
            < T1, T2 > ::result_type  operator ()( const  T1 &  t1,  const  T2 &  t2)  const
                  
            {
                      
            return  FuncType::execute(l(t1, t2), r(t1, t2));
                  }

             }
            ;

            很完美不是么,unary_op/binary_op繼承了Rettype, 也就擁有了該類所定一個全部result_x, 同時使用FuncType來執行運算符操作,很漂亮
            比如要支持操作符operator+,則需要寫一行
             DECLARE_META_BIN_FUNC(+, add, T1)
            那么binary_op<Left, Right, value_return, meta_add>就自然是operator+(雙目)的functor,不需要自己手動實現。
            停!不要陶醉在這美妙的幻覺中!
            如果把這段代碼拿到VC7或VC8下編譯,你會得到很有趣的結果。。。
            好了,這不是我們的錯,但是確實我們應該解決它。
            這實際上是vc的bug,解決方法是不要去使用typename Rettype::template result_2<T1, T2>::result_type這樣的形式。(感謝vbvan)
            下面是修改過的unary_op

            template < typename Left, typename OpClass, typename RetType >
             
            class  unary_op
             
            {
              Left l;
               
             
            public :

              unary_op(
            const  Left &  l) : l(l) {}

              template
            < typename T >
              
            struct  result_1
              
            {
               typedef typename RetType::template result_1
            < T > ::result_type result_type;
              }
            ;

              template
            < typename T1, typename T2 >
              
            struct  result_2
              
            {
               typedef typename RetType::template result_2
            < T1, T2 > ::result_type result_type;
              }
            ;

              template
            < typename T1, typename T2 >
              typename result_2
            < T1, T2 > ::result_type  operator ()( const  T1 &  t1,  const  T2 &  t2)  const
              
            {
               
            return  OpClass::execute(lt(t1, t2));
              }


              template
            < typename T >
              typename result_1
            < T > ::result_type  operator ()( const  T &  t)  const
              
            {
               
            return  OpClass::execute(lt(t));
              }


             }
            ;

            該方法避免直接使用RetType的result_x,而自己申明一個對應的result_x做一次中轉,雖然其實毫無意義,卻恰好避開了vc的bug
            好啦,現在才真正完美了。
            現在在picker里面就可以這么添加了:

            template < typename Right >
             picker
            < binary_op < Action, typename picker_maker < Right > ::result_type, ref_return < Action > , meta_add_assign >   >   operator += ( const  Right &  rt)  const
             
            {
              
            return  binary_op < Action, typename picker_maker < Right > ::result_type, ref_return < Action > , meta_add_assign > ( * this , rt);
             }

            有點長不是么?不過實際代碼量減少了很多,而且此后如果支持的參數上限發生變化,我們就只需要修改binary_op和unary_op就行了。

             


            十. bind
            既然都做到這份上了,我們順便把bind也做了吧,其實事情已經變得很簡單了。
            先來分析一下一段例子

            int  foo( int  x,  int  y) { return  x  -  y;}
            bind(foo, _1, constant(
            2 )( 1 )   // return -1
            bind(foo, _2, _1)( 3 6 )   // return foo(6, 3) == 3

            可見bind是一系列重載函數,返回某種functor,該functor的執行就是執行傳進bind的函數指針并正確的確定參數。
            我們來寫個簡單的。
            首先要知道一個函數的返回類型,我們使用一個trait來實現:
            對于函數對象類的版本:

             template < typename Func >
             
            struct  functor_trait
             
            {
              typedef typename Func::result_type result_type;
             }
            ;

            對于無參數函數的版本:

            template < typename Ret >
             
            struct  functor_trait < Ret ( * )() >
             
            {
              typedef Ret result_type;
             }
            ;

            對于單參數函數的版本:

            template < typename Ret, typename V1 >
             
            struct  functor_trait < Ret ( * )(V1) >
             
            {
              typedef Ret result_type;
             }
            ;

            對于雙參數函數的版本:

            template < typename Ret, typename V1, typename V2 >
             
            struct  functor_trait < Ret ( * )(V1, V2) >
             
            {
              typedef Ret result_type;
             }
            ;

            等等。。。
            然后我們就可以仿照value_return寫一個policy

            template < typename Func >
             
            struct  func_return
             
            {
              template
            < typename T >
              
            struct  result_1
              
            {
               typedef typename functor_trait
            < Func > ::result_type result_type;
              }
            ;

              template
            < typename T1, typename T2 >
              
            struct  result_2
              
            {
               typedef typename functor_trait
            < Func > ::result_type result_type;
              }
            ;
             }
            ;

            最后一個單參數binder就很容易寫出來了

            template < typename Func, typename aPicker >
             
            class  binder_1
             
            {
              Func fn;
              aPicker pk;
             
            public :

              template
            < typename T >
              
            struct  result_1
              
            {
               typedef typename func_return
            < Func > ::template result_1 < T > ::result_type result_type;
              }
            ;

              template
            < typename T1, typename T2 >
              
            struct  result_2
              
            {
               typedef typename func_return
            < Func > ::template result_2 < T1, T2 > ::result_type result_type;
              }
            ;

              binder_1(Func fn, 
            const  aPicker &  pk) : fn(fn), pk(pk) {}

              template
            < typename T >
              typename result_1
            < T > ::result_type  operator ()( const  T &  t)  const
              
            {
               
            return  fn(pk(t));
              }

              template
            < typename T1, typename T2 >
              typename result_2
            < T1, T2 > ::result_type  operator ()( const  T1 &  t1,  const  T2 &  t2)  const
              
            {
               
            return  fn(pk(t1, t2));
              }

             }
            ;

            一目了然不是么?
            最后實現bind
             

            template < typename Func, typename aPicker >
             picker
            < binder_1 < Func, aPicker >   >  bind( const  Func fn,  const  aPicker &  pk)
             
            {
              
            return  binder_1 < Func, aPicker > (fn, pk);
             }

            2個以上參數的bind可以同理實現。
            另外還可以照樣實現一系列binder來綁定類成員函數/變量,手法雷同,就不詳細介紹了。

            十一. phoenix
            Boost.phoenix可能知道的人不多,讓我們來看一段代碼吧: 

            for_each(v.begin(), v.end(),
             (
              do_
              [
               cout 
            <<  _1  <<   " "
              ]
              .while_(
            -- _1),
              cout 
            <<  var( " \n " )
              )
             );

            是不是華麗的讓人撞墻?其實這個比想象的好實現的多。還是照慣例分析一下吧:
            首先do_很明顯是個對象,該對象重載了operator[],接受一個functor作為參數,并返回另一個對象,該對象有一個成員函數while_,同樣接受一個functor作為參數,并返回一個functor, 最后2個functor用operator, 生成一個新的functor
            operator,的實現這里略過了,請參照前面的描述。
            那么我們就照著這個思路來實現吧:
             

            template < typename Cond, typename Actor >
             
            class  do_while
             

              Cond cd;
              Actor act;
             
            public :
              template
            < typename T >
              
            struct  result_1
              
            {
               typedef 
            int  result_type;
              }
            ;

              do_while(
            const  Cond &  cd,  const  Actor &  act) : cd(cd), act(act) {}

              template
            < typename T >
              typename result_1
            < T > ::result_type  operator ()( const  T &  t)  const
              
            {
               
            do
               
            {
                act(t);
               }

               
            while  (cd(t));
               
            return   0 ;
              }

             }
            ;

            這就是最終的functor,我略去了result_2和2個參數的operator().
            代碼很清晰,但是還是讓我來解釋一下為什么要用int作為返回類型。
            其實對于do-while語義,返回類型是無意義的,然而將其定義為void會影響在某些情況下return的簡潔性,因為return一個void是不合法的。
            因此我們將其定為int,并返回0,這樣減少了其它地方編碼的復雜度。
            下面就是產生這個functor的類:
             

            template < typename Actor >
             
            class  do_while_actor
             
            {
              Actor act;
             
            public :
              do_while_actor(
            const  Actor &  act) : act(act) {}

              template
            < typename Cond >
              picker
            < do_while < Cond, Actor >   >  while_( const  Cond &  cd)  const ;
             }
            ;

            簡單吧,注意到這個while_函數,它自動的生成了一個do_while對象。
            最后,是那個do_
             

            class  do_while_invoker
             
            {
             
            public :
              template
            < typename Actor >
              do_while_actor
            < Actor >   operator [](Actor act)  const
              
            {
               
            return  do_while_actor < Actor > (act);
              }

             }
            do_;

            好啦,現在明白do_[xxx].while_(xxx)是怎么工作的吧?
            同樣的,我們還可以做if_, while_, for_, switch_等。
            最后來說說怎么處理break和continue
            顯然break的語義超出了我們的能力范圍,然而卻是有一個東西很適合模擬其行為,那就是異常。
            具體實現手法這里就不羅嗦了。

            posted on 2006-06-09 13:23 shifan3 閱讀(2991) 評論(7)  編輯 收藏 引用 所屬分類: template 、BoostC++

            FeedBack:
            # re: 自己實現Lambda 2006-06-09 17:56 LOGOS
            boost庫真的是太華麗了  回復  更多評論
              
            # re: 自己實現Lambda 2007-01-01 13:51 test
            我覺得你像是寫看lambda 庫的心得
            看你文章前,我先研究了一下lambda庫,結論和你差不多  回復  更多評論
              
            # re: 自己實現Lambda 2007-01-02 18:25 Francis Arcanum
            那么。。。純屬巧合了
            我寫這篇文章的時候沒看過lambda的源代碼,僅僅是看過其文檔,會用罷了

            然而,我后來看boost.lambda的代碼發現,這中間還是有很多不同的,例如在針對于const的問題上,boost采用了用不帶const的T&推導的方法(導致字面值不能被直接傳入參數),而我采用了外部全部使用const,內部全部不使用const,中間用const_cast搭橋的方式(導致安全性下降)。
            我目前還是覺得boost的做法更好一些  回復  更多評論
              
            # re: [yc]自己實現Lambda[未登錄] 2007-08-21 20:10 Jarod
            請問作者都看了些什么書才對template的如此深入的了解。  回復  更多評論
              
            # re: [yc]自己實現Lambda 2007-08-22 10:29 Francis Arcanum
            @Jarod
            關于模板方面的書,C++ Template用來打基礎,Modern C++ Design用來進階很適合  回復  更多評論
              
            # re: [yc]自己實現Lambda 2007-08-22 11:55 eXile
            閣下的模板技術比我還牛....  回復  更多評論
              
            # re: [yc]自己實現Lambda 2008-08-07 16:05 littlewater
            看到中期這里,想不明白:
            picker是一個沒有默認構造函數的類,如何創建_1, _2支持picker<holder<1> >和picker<holder<2> >的定義呢?
              回復  更多評論
              
            亚洲伊人久久大香线蕉苏妲己| 亚洲国产成人精品女人久久久| 五月丁香综合激情六月久久| 亚洲综合熟女久久久30p| 欧美噜噜久久久XXX| 久久国产视频99电影| 久久婷婷五月综合成人D啪| 日本久久久久亚洲中字幕| 26uuu久久五月天| 久久99精品国产麻豆宅宅| 久久综合中文字幕| 亚洲色欲久久久综合网东京热| 国产精品久久久久国产A级| 欧美日韩精品久久久免费观看| 亚洲国产精品18久久久久久| 久久九九有精品国产23百花影院| 久久亚洲AV无码精品色午夜| 国产69精品久久久久99| 久久亚洲AV成人无码国产| 亚洲午夜精品久久久久久浪潮| 狠狠色丁香久久综合婷婷| 香蕉久久永久视频| www亚洲欲色成人久久精品| 国内精品九九久久久精品| 久久久黄色大片| 久久噜噜久久久精品66| 久久精品国产亚洲一区二区| 久久精品亚洲日本波多野结衣 | 97久久天天综合色天天综合色hd| 色综合久久天天综线观看| 国产一区二区三精品久久久无广告| 久久精品国产亚洲AV无码偷窥 | 久久精品aⅴ无码中文字字幕重口 久久精品a亚洲国产v高清不卡 | 欧美日韩精品久久免费| 久久综合给合综合久久| 久久97久久97精品免视看| 国产午夜福利精品久久| 久久精品国产91久久麻豆自制| 欧美午夜精品久久久久免费视 | 国产成年无码久久久久毛片| 亚洲精品国精品久久99热一|