一. 什么是Lambda %:3'4;jh%
所谓Lambda,简单的说就是快速的小函数生成。 >SoO4i8
在C++中,STL的很多算法都要求使用者提供一个函数对象。例如for_each函数,会要求用户提供一个表明“行为”的函数对象。以vector<bool>为例,如果想使用for_each对其中的各元素全部赋值为true,一般需要这么一个函数对象, d ;^
"(,2L,Zh
.k@^KY
2ev*CX6.
class filler SWs3SYJ\
{ WuM C^
public : %X>FVlPm
void operator ()( bool & i) const {i = true ;} 537?9
} ; hv>KX
~U$":~H[
<~{du ?4n
这样实现不但麻烦,而且不直观。而如果使用lambda,则允许用户使用一种直观和见解的方式来处理这个问题。以boost.lambda为例,刚才的问题可以这么解决: @TzvT3\q
o5swH6Y.)J
r|GY]9
M-J<n>hl
for_each(v.begin(), v.end(), _1 = true ); H(|AH;?ou
*%!M4&
T#EFXHPr
那么下面,就让我们来实现一个lambda库。 &gn-Wb?
2q PhLCeZ
|sIr?RL{C
Nxk(mec"
二. 战前分析 gKo%(6{n~
首先要说明的是,我并没有读过boost.lambda或其他任何lambda库的代码,因此如代码有雷同,纯属巧合。 O9s?h3
开始实现以前,首先要分析出大致的实现手法。先让我们来看几段使用Lambda的代码 M;jcUX_{
Wd8Ru/
<*(^{a.O
for_each(v.begin(), v.end(), _1 = 1 ); n2f6p<8A
/* --------------------------------------------- */ h2~4G)J
vector < int *> vp( 10 ); \X|sU:g
transform(v.begin(), v.end(), vp.begin(), & _1); iWA|8$u4gm
/* --------------------------------------------- */ C ^w)|2o}
sort(vp.begin(), vp.end(), * _1 > * _2); ]$A(9Pn"
/* --------------------------------------------- */
9QwKakci
int b = * find_if(v.begin, v.end(), _1 >= 3 && _1 < 5 ); l_{8+\`!
/* --------------------------------------------- */ .cDOl_z<:G
for_each(vp.begin(), vp.end(), cout << * _1 << ' \n ' ); 6C [E
/* --------------------------------------------- */ Sk}{E@
for_each(vp.begin(), vp.end(), cout << constant( ' \n ' ) << * _1); ;65D
$s-Y%gc
YKZa$@fA?
4!.(|h@
看了之后,我们可以思考一些问题: vLT0ETHg6
1._1, _2是什么? t2>fmQIQ
显然_1和_2都满足C++对于标识符的要求,可见_1和_2都是对象。 zMi; A6
2._1 = 1是在做什么? Y?d9l
既然_1是一个对象,那么_1的类必然重载了operator=(int)。那么operator=返回什么呢?该函数所返回的对象被传入for_each的第3个参数,可见其返回了一个函数对象。现在整个流程就很清楚了。_1 = 1调用了operator=,其返回了一个函数对象,该函数对象能够将参数1赋值为1。 !w39FfU{
Ok,回答了这两个问题之后,我们的思路就很清晰了。如果要实现operator=,那么至少要实现2个类,一个用于产生_1的对象,另一个用于代表operator=返回的函数对象。 mGjN_
j%IF2p2
7zemr>sIh
三. 动工 c>:R3^\lwx
首先实现一个能够范型的进行赋值的函数对象类: Lel|,mc`k2
. pzC5Ah
PG~$D];
72W
s
K"
template < typename T > %]2,&
class assignment k,yZ[n|`
{ G\de2Q"d:O
T value; ^T::-pN*
public : 457\&
assignment( const T & v) : value(v) {} Gf EX>
template < typename T2 > s$D ^ >0
T2 & operator ()(T2 & rhs) const { return rhs = value; } 6!'3oN{
} ; {X(:jAy
6Orum/|h
~Wo)?q8UY,
其中operator()被声明为模版函数以支持不同类型之间的赋值。 xG&)1sT#-\
然后我们就可以书写_1的类来返回assignment .Z:zZ_Ev
UcOk3{(z$q
l*\y
nw>8GivO
class holder U4aU}1RKz
{ ?T9(Vw
public : ydRC1~f0
template < typename T > bef_rH@`
assignment < T > operator = ( const T & t) const aT>'.*\ ]
{ S"wR%\NIp
return assignment < T > (t); ^?7`;/
} MjlP+; !
} ; <:SZAAoIV
V-<GT?
:8CvRO*<
由于该类是一个空类,因此我们可以在其后放心大胆的写上: a}%#*J)!
R"
;xvo*
static holder _1; T!Hb{Cg*
Ok,现在一个最简单的lambda就完工了。你可以写 d&ex5CU5
LaQ-=;(`
for_each(v.begin(), v.end(), _1 = 1 ); GrUCZ<S
而不用手动写一个函数对象。 JSW^dw&
4"gM<z
}ZYv~E'
C(00<~JC
四. 问题分析 b0lq\9
虽然基本上一个Lambda已经初步实现出来了,但是仔细想想,问题也是很多的。 >}+/{(K"E|
1, 我们现在是把_1和functor看成两个不同的存在,会导致代码的重复。 zIh['^3.n
2, 目前这个Lambda还无法实现如_1 = 2 = 3这样的链式操作。 G6T_O
3, 我们没有设计好如何处理多个参数的functor。 -$\+'
\
下面我们可以对这几个问题进行分析。 .zi_[
"?V0$-DR
五. 问题1:一致性 {phNds%
首先来看看1,合并_1和functor的最佳方法就是把_1本身也变成functor。那么_1的operator()会做什么事情呢?| -i0~]*
很明显,_1的operator()仅仅应该返回传进来的参数本身。 SHe49!RA'{
S:h{2{
struct holder !\7!3$w'8,
{ _d5QbTe
// z6*X%6,8
template < typename T > wK?vPS
T & operator ()( const T & r) const 7 @D@ucL
{ $<}$DH_Y
return (T & )r; Qk:Y2mL
} vX/T3WV
} ; !@}wDt
uG,5BV .M
这样的话assignment也必须相应改动: @7IIM{
RZXjgddL
template < typename Left, typename Right > A~)D[CV
class assignment y*qVc E
{ 4 o Fel.o
Left l; aDU<wxnSvO
Right r; ,J+}rPe"sf
public : ,U2*FZ["
assignment( const Left & l, const Right & r) : l(l), r(r) {} 3 2&;`]C
template < typename T2 > GPN]9
T2 & operator ()(T2 & rhs) const { return l(rhs) = r; } AE[b},-[
} ; \NPmym_6J
hgPa6Kd
同时,holder的operator=也需要改动: ;r<^a6B
h`^jyoF"(
template < typename T > X%
t1T4
assignment < holder, T > operator = ( const T & t) const 8A##\j)
{ ofm#'7P 0
return assignment < holder, T > ( * this , t); z&zP)>Pv
} "~sW"n(F_
'jWr<]3
好,这样holder也成为了一个functor,这为我们以后添加功能节省了很多代码。 d;boIP`M;
你可能也注意到,常数和functor地位也不平等。 !fE`4<