一. 什么是Lambda &X,6v
所谓Lambda,简单的说就是快速的小函数生成。 6%MM)Vj+u
在C++中,STL的很多算法都要求使用者提供一个函数对象。例如for_each函数,会要求用户提供一个表明“行为”的函数对象。以vector<bool>为例,如果想使用for_each对其中的各元素全部赋值为true,一般需要这么一个函数对象, 5k;}I|rg %
\"P$*y4Le
:ay`Id_tm
]?_V+F
class filler Ue=1NnRDkA
{ =(Y+u
public : L$?YbQo7
void operator ()( bool & i) const {i = true ;} A~;+P
} ; 2>)::9e4
P}vk5o'
,Y@4d79
这样实现不但麻烦,而且不直观。而如果使用lambda,则允许用户使用一种直观和见解的方式来处理这个问题。以boost.lambda为例,刚才的问题可以这么解决: 8Rnq
&8A
QEP|%$:i
Kc`#~-`,(
k)agbx
for_each(v.begin(), v.end(), _1 = true ); C#.27ah
G4%dah 5
A`V:r2hnb
那么下面,就让我们来实现一个lambda库。 ~n%]u! 6
Q
822 #
7b(r'b@N
W{t-UK
二. 战前分析 ^ R3g7 DG
首先要说明的是,我并没有读过boost.lambda或其他任何lambda库的代码,因此如代码有雷同,纯属巧合。 ]3
76F7
开始实现以前,首先要分析出大致的实现手法。先让我们来看几段使用Lambda的代码 X]s="^
-ug-rdXV
D 1(9/;9
for_each(v.begin(), v.end(), _1 = 1 ); HFX,EE
/* --------------------------------------------- */ _+<AxE9\
vector < int *> vp( 10 ); G#3$sz
transform(v.begin(), v.end(), vp.begin(), & _1); q)N^
/* --------------------------------------------- */ vAtR\Vh
sort(vp.begin(), vp.end(), * _1 > * _2); Er|j\(jM
/* --------------------------------------------- */ >iI_bcqF
int b = * find_if(v.begin, v.end(), _1 >= 3 && _1 < 5 ); kZ=yb-~
/* --------------------------------------------- */ K*5Ij]j&
for_each(vp.begin(), vp.end(), cout << * _1 << ' \n ' ); Y r8gKhv W
/* --------------------------------------------- */ S^r[%l<'n
for_each(vp.begin(), vp.end(), cout << constant( ' \n ' ) << * _1); .]/k#Hv
?}No'E1!I
ygxaT"3"=
(]$&.gE.F
看了之后,我们可以思考一些问题: Fyc":{Jd
1._1, _2是什么? A s8IjGNs{
显然_1和_2都满足C++对于标识符的要求,可见_1和_2都是对象。 F
7X] h
2._1 = 1是在做什么? !X1
KOG
既然_1是一个对象,那么_1的类必然重载了operator=(int)。那么operator=返回什么呢?该函数所返回的对象被传入for_each的第3个参数,可见其返回了一个函数对象。现在整个流程就很清楚了。_1 = 1调用了operator=,其返回了一个函数对象,该函数对象能够将参数1赋值为1。 v"dl6%D"
Ok,回答了这两个问题之后,我们的思路就很清晰了。如果要实现operator=,那么至少要实现2个类,一个用于产生_1的对象,另一个用于代表operator=返回的函数对象。 B
\.05<
US&:UzI.
B~%SB/eu
三. 动工 9w-;d=(Q
首先实现一个能够范型的进行赋值的函数对象类: MX7$f (Hy
O>IG7Ujl
"Jg*
/F
d V3R)
template < typename T > T5aeO^x
class assignment "MDy0Tj8EN
{ ~'LoIv20j)
T value; l>pnY%(A
public : =j8g6# 'u
assignment( const T & v) : value(v) {} uy([>8uu
template < typename T2 > p%5(Qqmlk
T2 & operator ()(T2 & rhs) const { return rhs = value; } p+Fh9N<F9
} ; UbP$WIrq
;e Mb$px
WDh*8!)
其中operator()被声明为模版函数以支持不同类型之间的赋值。 DK<}q1xi
然后我们就可以书写_1的类来返回assignment rR(\fX!dg
!
;R}=
G.qjw]Llf
J:\O .F#Fi
class holder 7/bF04~%
{ la{o<||Aq
public : lht :%Ts$
template < typename T > `91?^T;\F
assignment < T > operator = ( const T & t) const l(~NpT{=V
{ z[0t%]7l
return assignment < T > (t); ($[@'?Z1
} _:G>bU/^
} ; Yz>8 Nn '_
ZU5; w
8[IR;gZf
由于该类是一个空类,因此我们可以在其后放心大胆的写上: gO bP
20 )8e!jP
static holder _1; WU6F-{M"?
Ok,现在一个最简单的lambda就完工了。你可以写 TWU1@5?Ct
Kj+TPqXb
for_each(v.begin(), v.end(), _1 = 1 ); oi%IHX(`
而不用手动写一个函数对象。 ?IR+OCAA
LHq*E`
t=n@<1d
'^BTa6W}m
四. 问题分析 _j]vR
虽然基本上一个Lambda已经初步实现出来了,但是仔细想想,问题也是很多的。 _+qtH< F/
1, 我们现在是把_1和functor看成两个不同的存在,会导致代码的重复。 V/J-zH&
2, 目前这个Lambda还无法实现如_1 = 2 = 3这样的链式操作。 A~8-{F 31
3, 我们没有设计好如何处理多个参数的functor。
!-8y;,P
下面我们可以对这几个问题进行分析。 0~cbB
HCa EETk5
五. 问题1:一致性 sDXQ{*6a
首先来看看1,合并_1和functor的最佳方法就是把_1本身也变成functor。那么_1的operator()会做什么事情呢?| D#11
N^-K
很明显,_1的operator()仅仅应该返回传进来的参数本身。 |k)Nf+(}W
k'K 1zUBj
struct holder }Q_ }c9?
{ ;uqi
// - S%8
template < typename T > {?]&