一. 什么是Lambda J :(\o=5 5
所谓Lambda,简单的说就是快速的小函数生成。 `:O.g9
在C++中,STL的很多算法都要求使用者提供一个函数对象。例如for_each函数,会要求用户提供一个表明“行为”的函数对象。以vector<bool>为例,如果想使用for_each对其中的各元素全部赋值为true,一般需要这么一个函数对象, uNBhVsM6<
dF]8>jBOL
N)Kr4GC
@ xr
class filler 4 Z)]Cq*3
{ f`rz)C03
public : U#
B
void operator ()( bool & i) const {i = true ;} R/|{?:r?:x
} ; AE
_~DZ:%c
HE'8
y@JYkp>I
这样实现不但麻烦,而且不直观。而如果使用lambda,则允许用户使用一种直观和见解的方式来处理这个问题。以boost.lambda为例,刚才的问题可以这么解决: XjU; oh4:.
1]`HX=cl
/MtacR
^SCWT\E
for_each(v.begin(), v.end(), _1 = true ); )zV5KC{{
FR"^?z?}p
Xy}S}9
那么下面,就让我们来实现一个lambda库。 $c47cJO)W
[.,6~=}vP
-y<uAI g
4gENV{L
二. 战前分析 z(eAwmuli
首先要说明的是,我并没有读过boost.lambda或其他任何lambda库的代码,因此如代码有雷同,纯属巧合。 e84TLU?~
开始实现以前,首先要分析出大致的实现手法。先让我们来看几段使用Lambda的代码 S}O\<6&
CU_06A|}
mX_`rvYII
for_each(v.begin(), v.end(), _1 = 1 ); jXZNr
/* --------------------------------------------- */ --sb ;QG
vector < int *> vp( 10 ); RoRVu,1
transform(v.begin(), v.end(), vp.begin(), & _1); iKY&gnu"
/* --------------------------------------------- */ _AHVMsz@
sort(vp.begin(), vp.end(), * _1 > * _2); X_l,fu^C#$
/* --------------------------------------------- */ )v0vdAh'b
int b = * find_if(v.begin, v.end(), _1 >= 3 && _1 < 5 );
(5_(s`q.
/* --------------------------------------------- */ `_)dEu
for_each(vp.begin(), vp.end(), cout << * _1 << ' \n ' ); ;0gpS y$#
/* --------------------------------------------- */ mo$*KNW%\
for_each(vp.begin(), vp.end(), cout << constant( ' \n ' ) << * _1); +Z*%,m=N(
I),8EEf\
4[q *7m
JK`P
mp>
看了之后,我们可以思考一些问题: 6B{Awm@v}X
1._1, _2是什么? .5xM7,
显然_1和_2都满足C++对于标识符的要求,可见_1和_2都是对象。 0f1#TgX
2._1 = 1是在做什么? X9HI@M]h
既然_1是一个对象,那么_1的类必然重载了operator=(int)。那么operator=返回什么呢?该函数所返回的对象被传入for_each的第3个参数,可见其返回了一个函数对象。现在整个流程就很清楚了。_1 = 1调用了operator=,其返回了一个函数对象,该函数对象能够将参数1赋值为1。 OpQa!
Ok,回答了这两个问题之后,我们的思路就很清晰了。如果要实现operator=,那么至少要实现2个类,一个用于产生_1的对象,另一个用于代表operator=返回的函数对象。 IIZsN*^
_I!&w!3oM
9n7d
"XD2
三. 动工 0<9TyN6
首先实现一个能够范型的进行赋值的函数对象类: B"v=Fr[
DM9 5Il[/
uX[
"w|
Ex3woT-
template < typename T > }dM^6
Kd%
class assignment qQ_QF
{ JhcS
T value; GZo4uwG@a
public : KK6YA
assignment( const T & v) : value(v) {} ?Dm&A$r
template < typename T2 > qfU3Cwy
T2 & operator ()(T2 & rhs) const { return rhs = value; } !:5n
} ; ]u ';zJ.
b'YbHUyu
M&dtXG8<^
其中operator()被声明为模版函数以支持不同类型之间的赋值。 *gn*S3Is[j
然后我们就可以书写_1的类来返回assignment }0G Ab2
-tQ|&fl
.w~USJ=X
)EoG@:[
class holder R7i*f/m
{ A-FwNo2"%
public : xjN~Y D:
template < typename T > Tx(R3B+u7
assignment < T > operator = ( const T & t) const wah`
{ "6i9 f$N
return assignment < T > (t); 4SYN$?.Mp
} L/I-(08!Y:
} ; 0bE_iu>f'
&bRH(yF
KJiwM(o
由于该类是一个空类,因此我们可以在其后放心大胆的写上: YaU A}0cW
6_Kz}PQ
static holder _1; J"y@n~*0
Ok,现在一个最简单的lambda就完工了。你可以写 bBX~ZWw
LHb{9x
for_each(v.begin(), v.end(), _1 = 1 ); QS}=oOR@k
而不用手动写一个函数对象。 D }\`5L<
~CA+'e%~~
gi)/iz `
he Wb(E&
四. 问题分析 pP @#|T
虽然基本上一个Lambda已经初步实现出来了,但是仔细想想,问题也是很多的。 d\v _!7
1, 我们现在是把_1和functor看成两个不同的存在,会导致代码的重复。 |};~YMH
2, 目前这个Lambda还无法实现如_1 = 2 = 3这样的链式操作。 5h1j.t!
3, 我们没有设计好如何处理多个参数的functor。 w9%gaK;
下面我们可以对这几个问题进行分析。 ,#G@ri:B
Z=|@76
五. 问题1:一致性 _OY ;SJ(
首先来看看1,合并_1和functor的最佳方法就是把_1本身也变成functor。那么_1的operator()会做什么事情呢?| 5IMH G%W7
很明显,_1的operator()仅仅应该返回传进来的参数本身。 ZeO>Ag^
NmQ]qv
struct holder 4jpF^&y7u^
{ :.cX3dP@
// T*IudxW
template < typename T > i,'~Ds
T & operator ()( const T & r) const yrjm0BM#
{ IQDWH/c
return (T & )r; |Xag:hof
} Ut+m m\7
} ; bA)Xjq)Rr
^?2txLv,6
这样的话assignment也必须相应改动: { at;
U@o
/y 0 )r.R
template < typename Left, typename Right > fp7Qb $-A
class assignment 1f=L8Dr
{ H2]I__t/u
Left l; NQG"}=KA
Right r; Cv| :.y
public : 0\+Qi?&
assignment( const Left & l, const Right & r) : l(l), r(r) {} 9YJb~tuZ73
template < typename T2 > b%kh:NV{S
T2 & operator ()(T2 & rhs) const { return l(rhs) = r; } J: LSGj;R
} ; URAipLvN
Xk2
75Y
同时,holder的operator=也需要改动: Y%faf.$/9
TDoYp
template < typename T > GYYro&aq{
assignment < holder, T > operator = ( const T & t) const ?]t8$^m,;
{ V/Q6v
YX
return assignment < holder, T > ( * this , t); /a
q%l]hQ@
} z,9qAts?mh
&[YG\8sxWa
好,这样holder也成为了一个functor,这为我们以后添加功能节省了很多代码。 gvC2\k{
你可能也注意到,常数和functor地位也不平等。 -4Xr5j%o
a5'QL(IX
return l(rhs) = r; #xc[)Y,W
在这一句中,r没有调用operator()而l调用了。这样以后就要不时的区分常数和functor,是不良的设计。 _VlNZ/V
那么我们仿造holder的做法实现一个常数类: bYtF#Y
MiC&av
template < typename Tp > %;<