一. 什么是Lambda qeQTW@6
F
所谓Lambda,简单的说就是快速的小函数生成。 h\Op|#gIT
在C++中,STL的很多算法都要求使用者提供一个函数对象。例如for_each函数,会要求用户提供一个表明“行为”的函数对象。以vector<bool>为例,如果想使用for_each对其中的各元素全部赋值为true,一般需要这么一个函数对象, #AyM!
@bmu4!"d
{[hV['Awv
f5
wn`a~h
class filler hx+a.N
{ kMo;<Z
public : U;i:k%Bzy
void operator ()( bool & i) const {i = true ;} pTOS}A[dh
} ; ?q7VB
@Q!f^
{O5;V/00}
这样实现不但麻烦,而且不直观。而如果使用lambda,则允许用户使用一种直观和见解的方式来处理这个问题。以boost.lambda为例,刚才的问题可以这么解决: f6PXcV
64#~ p)
M cNj TD
vs{i2!^
for_each(v.begin(), v.end(), _1 = true ); RxAWX?9Z
^.mQ~F
<6mXlK3N0
那么下面,就让我们来实现一个lambda库。 :)g=AhBF
1'"o; a]k/
L/%3_,
~4=4Ks0
二. 战前分析 -86 9$
首先要说明的是,我并没有读过boost.lambda或其他任何lambda库的代码,因此如代码有雷同,纯属巧合。 REW
*6:
开始实现以前,首先要分析出大致的实现手法。先让我们来看几段使用Lambda的代码 {b<p~3%+Hc
9TO
2Q|Vg*x\U
for_each(v.begin(), v.end(), _1 = 1 ); 3VCyq7B^
/* --------------------------------------------- */ g4=}].
vector < int *> vp( 10 ); 0jrcXN~
transform(v.begin(), v.end(), vp.begin(), & _1); #i7!
/* --------------------------------------------- */ m qPWCFP
sort(vp.begin(), vp.end(), * _1 > * _2); 7{D+\i
/* --------------------------------------------- */ o83HR[
int b = * find_if(v.begin, v.end(), _1 >= 3 && _1 < 5 ); i'L7t!f}o
/* --------------------------------------------- */
M)Yu^
for_each(vp.begin(), vp.end(), cout << * _1 << ' \n ' ); 3_J9SwtN
/* --------------------------------------------- */ W;,UhE
for_each(vp.begin(), vp.end(), cout << constant( ' \n ' ) << * _1); |m"2B]"@
-F4CHpua
O#H `/z
YCeE?S1gk3
看了之后,我们可以思考一些问题: ZJP.-` U
1._1, _2是什么? TiCp2Rsz
显然_1和_2都满足C++对于标识符的要求,可见_1和_2都是对象。 gA2Il8K
2._1 = 1是在做什么? .7g^w+W
既然_1是一个对象,那么_1的类必然重载了operator=(int)。那么operator=返回什么呢?该函数所返回的对象被传入for_each的第3个参数,可见其返回了一个函数对象。现在整个流程就很清楚了。_1 = 1调用了operator=,其返回了一个函数对象,该函数对象能够将参数1赋值为1。 j Z3N+_J1
Ok,回答了这两个问题之后,我们的思路就很清晰了。如果要实现operator=,那么至少要实现2个类,一个用于产生_1的对象,另一个用于代表operator=返回的函数对象。 v8y77:
+'=^/!
?T$i
三. 动工 _q)`Y:2
首先实现一个能够范型的进行赋值的函数对象类: n~8-+$6OR
'ujtw:Z:
udqGa)&0
Z2PLm0%:
template < typename T > d{9rEB?
class assignment PP[{c
{ "h_n/}r=
T value; ,`P,))
public : Q6MDhv,
assignment( const T & v) : value(v) {} W7l/{a
@
template < typename T2 > *VIM!/YW
T2 & operator ()(T2 & rhs) const { return rhs = value; } e l'^9K
} ; 6y%BJU.I
UI<'T3b
hs2f3;)
其中operator()被声明为模版函数以支持不同类型之间的赋值。 zIH[
:
然后我们就可以书写_1的类来返回assignment
:?@d\c'
y:iE'SRRK6
VpWax]'
A8e b{qv
class holder [9z<*@$-
{
_"%d9B
public : ^KF
template < typename T > $*xnq%A
assignment < T > operator = ( const T & t) const Z#w1,n88
{ Fu )V2[TY
return assignment < T > (t); |; $fy-
} ^-4mZXAy1|
} ; AcrbR&cvG
m3F.-KPO
}-V .upl
由于该类是一个空类,因此我们可以在其后放心大胆的写上: ?j?{}Z
3yr{B Xn
static holder _1; ^^jF*)DT@
Ok,现在一个最简单的lambda就完工了。你可以写 @2CYv>
l"IBt:
for_each(v.begin(), v.end(), _1 = 1 ); %Q1v8l.}
而不用手动写一个函数对象。 R@=ve
%a-
Rk"VFe>r
viD+~j18
#ZCgpg$wM
四. 问题分析 67 7p9{:
虽然基本上一个Lambda已经初步实现出来了,但是仔细想想,问题也是很多的。 0w8Id
. ,
1, 我们现在是把_1和functor看成两个不同的存在,会导致代码的重复。 <rRmbFH#
2, 目前这个Lambda还无法实现如_1 = 2 = 3这样的链式操作。 15iCJ p
3, 我们没有设计好如何处理多个参数的functor。 vFL3eu#
下面我们可以对这几个问题进行分析。 ,":"Op61
Tx/
五. 问题1:一致性
Ca@[]-_H
首先来看看1,合并_1和functor的最佳方法就是把_1本身也变成functor。那么_1的operator()会做什么事情呢?| -R~;E[
{%
很明显,_1的operator()仅仅应该返回传进来的参数本身。 O7s0M?4
#T#&qo#
struct holder z.e%AcX
{ 1
YMaUyL
1
// &^ =t%A%#
template < typename T > 0AJ6g@t[
T & operator ()( const T & r) const e1~C>
{ wy&