青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 31  文章 - 128  trackbacks - 0
<2006年6月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

常用鏈接

留言簿(5)

隨筆分類(38)

隨筆檔案(31)

收藏夾(4)

College

High School

最新隨筆

搜索

  •  

積分與排名

  • 積分 - 56937
  • 排名 - 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,鏈式操作就簡單多了。現在唯一要做的事情就是讓所有的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 閱讀(3020) 評論(7)  編輯 收藏 引用 所屬分類: templateBoostC++

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> >的定義呢?
  回復  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
              久久精品一区二区国产| 亚洲福利视频一区二区| 亚洲欧美日韩成人| 日韩亚洲成人av在线| 亚洲精品国产视频| 亚洲高清视频一区二区| 老司机免费视频一区二区| 欧美一级日韩一级| 免费成人毛片| 亚洲人成人77777线观看| 欧美激情日韩| 亚洲日本电影在线| 亚洲视频播放| 久久久久国产精品一区三寸 | 国产午夜精品麻豆| 曰本成人黄色| 日韩一二三在线视频播| 亚洲精品一区二区三区99| 亚洲午夜女主播在线直播| 在线日韩av| 欧美肥婆在线| 老司机午夜精品视频| 欧美久久久久久久| 一区二区免费在线观看| 亚洲国产一区二区a毛片| 亚洲一区三区视频在线观看 | 老司机亚洲精品| 久久久久国产精品人| 欧美高清视频www夜色资源网| 欧美日韩在线播放三区四区| 国产自产在线视频一区| 亚洲人成网站精品片在线观看| 亚洲综合第一页| 一区二区免费在线观看| 国产情人综合久久777777| 91久久一区二区| 久久久人成影片一区二区三区观看 | 欧美14一18处毛片| 欧美影片第一页| 国产日韩欧美在线视频观看| 99精品国产99久久久久久福利| 久久亚洲美女| 久久综合亚州| 99精品国产在热久久下载| 欧美成人精品福利| 久久亚洲一区| 亚洲精品一区二区三区99| 欧美国产精品人人做人人爱| 久久躁日日躁aaaaxxxx| 一区精品在线| 亚洲人成啪啪网站| 欧美视频不卡中文| 久久久久国色av免费观看性色| 欧美一区二区日韩| 亚洲精品国产精品国自产在线| 亚洲韩国一区二区三区| 欧美精品午夜视频| 欧美一区二区免费视频| 亚洲欧美日韩精品久久亚洲区| 亚洲视频在线视频| 亚洲高清视频在线| 亚洲日本中文字幕免费在线不卡| 国产精品毛片大码女人| 久久综合久久综合这里只有精品| 美女黄色成人网| 亚洲一区www| 狂野欧美激情性xxxx欧美| 亚洲自拍偷拍网址| 欧美国产日本高清在线| 久久久久综合一区二区三区| 欧美三区视频| 亚洲人成网站999久久久综合| 国产欧美日韩视频一区二区| 亚洲精品日韩一| 亚洲精品久久久蜜桃| 久久天天躁狠狠躁夜夜爽蜜月| 欧美一级成年大片在线观看| 国产精品wwwwww| 99热这里只有成人精品国产| 亚洲经典三级| 美女诱惑一区| 欧美黄色aa电影| 亚洲精品女人| 欧美啪啪一区| 日韩午夜免费视频| 亚洲一区日韩在线| 国产日韩一区欧美| 久久久天天操| 亚洲人体影院| 久久国产一二区| 国产一区二区三区四区在线观看| 久久黄色级2电影| 欧美成人亚洲成人日韩成人| 日韩视频永久免费| 欧美新色视频| 亚洲精品一二三| 一区二区久久| 国产精品高潮呻吟久久av黑人| 在线视频精品| 欧美高清在线一区| 国产精品99久久久久久宅男| 国产精品卡一卡二| 久久综合色综合88| 欧美激情第9页| 亚洲茄子视频| 亚洲欧洲一区二区在线播放 | 在线视频日韩| 亚洲电影第1页| 国产伪娘ts一区 | 国产精品私人影院| 美日韩丰满少妇在线观看| 久久都是精品| 激情懂色av一区av二区av| 最新热久久免费视频| 久久精品一区蜜桃臀影院| 亚洲精品一品区二品区三品区| 国产精品区一区二区三区| 蜜乳av另类精品一区二区| 欧美一区二区三区另类| 中文国产亚洲喷潮| 亚洲免费高清| 99热在线精品观看| 亚洲美女91| 亚洲一区二区三区欧美 | 亚洲欧美视频| 日韩午夜电影av| 亚洲伦理在线免费看| 亚洲精品老司机| 午夜精品美女自拍福到在线 | 久久久久久久久岛国免费| 性欧美办公室18xxxxhd| 久久成人精品一区二区三区| 欧美在线视频在线播放完整版免费观看| 一区二区三区精品视频| 亚洲一区欧美二区| 久久国产精品久久久| 老色批av在线精品| 最新日韩在线视频| 亚洲一区免费观看| 麻豆成人精品| 国产精品人人爽人人做我的可爱 | 国产精品成人一区二区网站软件 | 久久大逼视频| 欧美大色视频| 国产精品区一区二区三区| 韩国女主播一区| 亚洲网站啪啪| 欧美一区2区视频在线观看| 久久夜精品va视频免费观看| 日韩网站在线| 欧美久久久久久蜜桃| 尤妮丝一区二区裸体视频| 亚洲欧美久久久| 亚洲精品少妇| 欧美精品一区二区三区很污很色的 | 国产精品护士白丝一区av| 亚洲国产经典视频| 欧美激情国产日韩| 久久精品论坛| 亚洲国产一区二区在线| 欧美成人伊人久久综合网| 久久久7777| 亚洲激情中文1区| 亚洲精品综合精品自拍| 欧美日韩精品一区二区三区| 亚洲欧美日本伦理| 性视频1819p久久| 亚洲福利专区| 亚洲精品黄色| 国产精品豆花视频| 欧美在线国产| 免费日韩精品中文字幕视频在线| 亚洲欧洲精品一区二区精品久久久| 亚洲精品一区二区三区蜜桃久| 欧美性事在线| 欧美激情久久久久久| 欧美特黄一级| 久久久噜噜噜久久中文字免| 欧美久久久久| 亚洲精品国产欧美| 久久国产精品亚洲va麻豆| 欧美影院午夜播放| 美女久久网站| 欧美a级片一区| 欧美精品一区二区三区久久久竹菊| 国产亚洲精品资源在线26u| 一区二区三区国产| 亚洲国产欧美在线人成| 欧美va亚洲va日韩∨a综合色| 亚洲国产欧美一区| 久久久久久9999| 99精品欧美一区| 亚洲视频每日更新| 麻豆成人小视频| 欧美日韩精品一区二区天天拍小说| 欧美日韩亚洲高清一区二区| 亚洲区国产区| 亚洲黄色在线看| 欧美精品久久久久久久免费观看| 91久久久在线|