一. 什么是Lambda #L[Atx
所谓Lambda,简单的说就是快速的小函数生成。 t"JfqD E
在C++中,STL的很多算法都要求使用者提供一个函数对象。例如for_each函数,会要求用户提供一个表明“行为”的函数对象。以vector<bool>为例,如果想使用for_each对其中的各元素全部赋值为true,一般需要这么一个函数对象, 7$}lkL
jD'\\jAUdm
jSpmE
;S2^f;q~$
class filler B0nkHm.Sj
{ Ws.F=kS>h
public : I@7^H48\
void operator ()( bool & i) const {i = true ;} #.#T+B+9
} ; ZVk_qA%
/oE@F178
\_CC6J0k
这样实现不但麻烦,而且不直观。而如果使用lambda,则允许用户使用一种直观和见解的方式来处理这个问题。以boost.lambda为例,刚才的问题可以这么解决: [y64%|m
d#Ql>PrY
l>H#\MR
Z[Uz~W6M]
for_each(v.begin(), v.end(), _1 = true ); NKh"x&R
E<D45C{DP
3|l+&LF!IC
那么下面,就让我们来实现一个lambda库。 T"XZ[q
$x#Y\dpS
`a98+x?JF
Ry r2
二. 战前分析 /vBOf;L
首先要说明的是,我并没有读过boost.lambda或其他任何lambda库的代码,因此如代码有雷同,纯属巧合。 68W&qzw.[r
开始实现以前,首先要分析出大致的实现手法。先让我们来看几段使用Lambda的代码 FE" ksi 9
F@)wi0
~UEft
for_each(v.begin(), v.end(), _1 = 1 ); ^4h/6^b0c
/* --------------------------------------------- */ b~WiE?
vector < int *> vp( 10 ); bK<'J=#1
transform(v.begin(), v.end(), vp.begin(), & _1); Mb"i}Yt{
/* --------------------------------------------- */ gW$X8ECX
sort(vp.begin(), vp.end(), * _1 > * _2); `o)rAD^e
/* --------------------------------------------- */ %N=-i]+Id
int b = * find_if(v.begin, v.end(), _1 >= 3 && _1 < 5 ); oj;Rh!O
/* --------------------------------------------- */ josc
for_each(vp.begin(), vp.end(), cout << * _1 << ' \n ' ); C`%cPl
/* --------------------------------------------- */ m\O<Yc keA
for_each(vp.begin(), vp.end(), cout << constant( ' \n ' ) << * _1); 9)];l?l
+MvcW.W~
h/mmV:v
pa`"f&JO
看了之后,我们可以思考一些问题: ( Y'q%$
1._1, _2是什么? `XE8[XY
显然_1和_2都满足C++对于标识符的要求,可见_1和_2都是对象。 V80g+)|
2._1 = 1是在做什么? :Bz*vH
既然_1是一个对象,那么_1的类必然重载了operator=(int)。那么operator=返回什么呢?该函数所返回的对象被传入for_each的第3个参数,可见其返回了一个函数对象。现在整个流程就很清楚了。_1 = 1调用了operator=,其返回了一个函数对象,该函数对象能够将参数1赋值为1。 ~K&ko8
Ok,回答了这两个问题之后,我们的思路就很清晰了。如果要实现operator=,那么至少要实现2个类,一个用于产生_1的对象,另一个用于代表operator=返回的函数对象。 S[n;u-U
;r B2Q H]
U4w^eWzP
三. 动工 +.!
F]0ju
首先实现一个能够范型的进行赋值的函数对象类: xi
%u)p
8rx?mX,}
,-rOfk\u
5k)/SAU0
template < typename T > a;r,*zZ="
class assignment jhr:QS/9
{ [D=ba=r0X
T value; j(AN]g:
public : xRuAt/aC
assignment( const T & v) : value(v) {} iOYC1QFi?
template < typename T2 > & w&JE]$ 5
T2 & operator ()(T2 & rhs) const { return rhs = value; } o $7:*jU
} ; :D\M.A
xKi:
2
q@1b{q#C5
其中operator()被声明为模版函数以支持不同类型之间的赋值。 fzT|{vG8
然后我们就可以书写_1的类来返回assignment ds}: t.3}6
]+u`E
g\[?U9qN
ABuK`(f.
class holder R7+3$F5B
{ 2? 9*V19yu
public : LZG?M|(6D
template < typename T > _lcx?IV
assignment < T > operator = ( const T & t) const k)U9%Pr
{ G~b/!clN
return assignment < T > (t); 5?9K%x'b
} efW<
} ; l|TiUjs
2uujA*
^
[Q9#44@{S;
由于该类是一个空类,因此我们可以在其后放心大胆的写上: Cak`}J 2
qS1byqq78l
static holder _1; 2_k2t
?
Ok,现在一个最简单的lambda就完工了。你可以写 jrJ!A(<)
u*u3<YQ
for_each(v.begin(), v.end(), _1 = 1 ); 6AD#x7drj
而不用手动写一个函数对象。 #29m <f_n
uJhB>/Og
" iAwD8-
}22h)){n#Y
四. 问题分析 V9
Z
虽然基本上一个Lambda已经初步实现出来了,但是仔细想想,问题也是很多的。 zmaf@T
1, 我们现在是把_1和functor看成两个不同的存在,会导致代码的重复。 }rK9M$2]u
2, 目前这个Lambda还无法实现如_1 = 2 = 3这样的链式操作。 U?]}K S;6
3, 我们没有设计好如何处理多个参数的functor。 _-mSK/Z
下面我们可以对这几个问题进行分析。 <~s{&cL!%#
*f<+yF{=A
五. 问题1:一致性 .S4c<pMap
首先来看看1,合并_1和functor的最佳方法就是把_1本身也变成functor。那么_1的operator()会做什么事情呢?| >8%<ML
很明显,_1的operator()仅仅应该返回传进来的参数本身。 t*J*?Ma
XLQt>y)
struct holder ul@G{N{L
{ lqdil l\
// Ilf;Q(*$>>
template < typename T > K X0{dizZ
T & operator ()( const T & r) const nD#QC=}
{ W5a7HkM
return (T & )r; V&e9?5@
} &}}UdJ`
} ; fib#)KE
d!>.$|b
这样的话assignment也必须相应改动: vNo(`~]c
l5;
SY
template < typename Left, typename Right > TQhu$z<