一. 什么是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 、
Boost 、
C++