一. 什么是Lambda CAbeb+O
所谓Lambda,简单的说就是快速的小函数生成。 gMGX)Y ,=/
在C++中,STL的很多算法都要求使用者提供一个函数对象。例如for_each函数,会要求用户提供一个表明“行为”的函数对象。以vector<bool>为例,如果想使用for_each对其中的各元素全部赋值为true,一般需要这么一个函数对象, }{R?i,j(
CFLWo1
UJ/=RBfkJ
6njwrqo
class filler %nRz~3X|+v
{ F}f/cG<X
public : c'wxCqnE
void operator ()( bool & i) const {i = true ;} Y<]A5cm
} ; w$aiVOjgT
X6T*?t3!9[
8_d>=*(
这样实现不但麻烦,而且不直观。而如果使用lambda,则允许用户使用一种直观和见解的方式来处理这个问题。以boost.lambda为例,刚才的问题可以这么解决: dR9[K4`p/
m]7oTmS
#FZoi:'Q
4x2
;@Pd
for_each(v.begin(), v.end(), _1 = true ); !08\w@
>FR;Ux~a
A-&'/IHR"B
那么下面,就让我们来实现一个lambda库。 r1jsw j%7
`kRv+Qwfa
^Fn~@'
B24,;2J
二. 战前分析 _^k9!Vjo
首先要说明的是,我并没有读过boost.lambda或其他任何lambda库的代码,因此如代码有雷同,纯属巧合。 @@1Sxv_
开始实现以前,首先要分析出大致的实现手法。先让我们来看几段使用Lambda的代码 `|rr<Tsy\
[U^@Bk h
pzQWr*5a
for_each(v.begin(), v.end(), _1 = 1 ); kKFhbHUZa
/* --------------------------------------------- */ (}4]U=/nV
vector < int *> vp( 10 ); ![ce=9@t<
transform(v.begin(), v.end(), vp.begin(), & _1); [X\<C '<
/* --------------------------------------------- */ ~+~^c|
sort(vp.begin(), vp.end(), * _1 > * _2); )B!64'|M
/* --------------------------------------------- */ F?!X<N{
int b = * find_if(v.begin, v.end(), _1 >= 3 && _1 < 5 ); 1.U9EuI
/* --------------------------------------------- */ 1v?|n8
for_each(vp.begin(), vp.end(), cout << * _1 << ' \n ' ); @ptE&m
/* --------------------------------------------- */ S^,q{x*T
for_each(vp.begin(), vp.end(), cout << constant( ' \n ' ) << * _1); &gr)U3w
O>M4%p
#~I.F4
'QP~uK
看了之后,我们可以思考一些问题: q83!PI
1._1, _2是什么? Y)ig:m]#
显然_1和_2都满足C++对于标识符的要求,可见_1和_2都是对象。 ~Pm[Ud
2._1 = 1是在做什么? KE_GC ;bQ
既然_1是一个对象,那么_1的类必然重载了operator=(int)。那么operator=返回什么呢?该函数所返回的对象被传入for_each的第3个参数,可见其返回了一个函数对象。现在整个流程就很清楚了。_1 = 1调用了operator=,其返回了一个函数对象,该函数对象能够将参数1赋值为1。 -Wt(t2
Ok,回答了这两个问题之后,我们的思路就很清晰了。如果要实现operator=,那么至少要实现2个类,一个用于产生_1的对象,另一个用于代表operator=返回的函数对象。 ?xT ^9
C)RJjaOr
ds#om2)
三. 动工 ol7^T
首先实现一个能够范型的进行赋值的函数对象类: TwT@_~IM
<y!(X"n`
.szc-r{
/7o{%~O
template < typename T > 9R1S20O
class assignment u&npUw^Va
{ ,K-?M5(n9
T value; B7u4e8(E*
public : t*Xo@KA
assignment( const T & v) : value(v) {} q=J8SvSRl
template < typename T2 > hgmo b"o
T2 & operator ()(T2 & rhs) const { return rhs = value; } u]uUm1Er
} ; |/M^q{h&7s
A4mnm6Tf
Ltrw)H}
其中operator()被声明为模版函数以支持不同类型之间的赋值。 PX$_."WA
然后我们就可以书写_1的类来返回assignment a^>e|Eq|
H7}@56
6$y$ VeW
'k1vV
class holder |{j\7G*5
{ *$Tz g!/
public : .271at#-
template < typename T > p4sU:
assignment < T > operator = ( const T & t) const 7A6: *
{ tDQo1,(oY
return assignment < T > (t); z"PU`v
} Vgg'5o&.
} ; SU$%nK )
7W7yjG3g
z<~yns`Y.
由于该类是一个空类,因此我们可以在其后放心大胆的写上: J^xIfV~zt
f.{/PL
static holder _1; !hc#il'g].
Ok,现在一个最简单的lambda就完工了。你可以写 l(j._j~p
}^"#&w3<
for_each(v.begin(), v.end(), _1 = 1 ); ysDGF@wZC
而不用手动写一个函数对象。 KM&bu='L^
8_h:_7e
!gX(Vh*k
Y2&hf6BE
四. 问题分析 }
>zl
虽然基本上一个Lambda已经初步实现出来了,但是仔细想想,问题也是很多的。 &f_ua)cyY
1, 我们现在是把_1和functor看成两个不同的存在,会导致代码的重复。 ` &{
2, 目前这个Lambda还无法实现如_1 = 2 = 3这样的链式操作。 /8Xd2-
3, 我们没有设计好如何处理多个参数的functor。 <3WaFi u
下面我们可以对这几个问题进行分析。 rT/4w#_3
8HxtmFqG
五. 问题1:一致性 pY"&=I79tb
首先来看看1,合并_1和functor的最佳方法就是把_1本身也变成functor。那么_1的operator()会做什么事情呢?| &3~_9+
很明显,_1的operator()仅仅应该返回传进来的参数本身。 ;]A:(HSZj
U+7!Vpq
struct holder C<"b99\2`
{ \1[v-hvK
// !`S61~gE
template < typename T > KpF/g[m
T & operator ()( const T & r) const 5y8ajae:
{ e00s*LdC
return (T & )r; *m.4)2u=
} =t!$72g\
} ; nR#'BBlI
f`Wces=5
这样的话assignment也必须相应改动: +|c1G[Jh
eGE[4Z
template < typename Left, typename Right > b8~7C4
class assignment #Ab,h#f*7
{ &C&?kS(
Left l; &|#z" E^-
Right r; I>n2# -8
public : hutdw>
assignment( const Left & l, const Right & r) : l(l), r(r) {} lDF26<<\`
template < typename T2 > ~X2
cTG!,
T2 & operator ()(T2 & rhs) const { return l(rhs) = r; } ov%.+5 P
} ; Y. 1dk
^^+vt8|
同时,holder的operator=也需要改动: sA1 XtO<&7
2 i:tPe&
template < typename T > ]<<+#Rg
assignment < holder, T > operator = ( const T & t) const :(Uz`k7
{ b+!I_g4P
return assignment < holder, T > ( * this , t); <cNg_ZZ;8
} gVU&Yl~/^
rG"QK!R5
好,这样holder也成为了一个functor,这为我们以后添加功能节省了很多代码。 iD`>Bt7gD
你可能也注意到,常数和functor地位也不平等。 ,.-85isco
jB -wJNP/
return l(rhs) = r; }$D{YHF
在这一句中,r没有调用operator()而l调用了。这样以后就要不时的区分常数和functor,是不良的设计。 P d)<Iw^<
那么我们仿造holder的做法实现一个常数类: -$@4e|e%a
W;y ,Xs
template < typename Tp > g6l&;S40
class constant_t OaCp3No
{ eW.[M ?,
const Tp t; yr,Oq~e
public : wW1>#F
constant_t( const Tp & t) : t(t) {} !dZpV~g0
template < typename T > a/s6|ri`0
const Tp & operator ()( const T & r) const u(b Pdf@kz
{ 5l,Q=V^@l
return t; yE>f.|(
} $,DX^I%!
} ; 0{zA6Xu
+E8}5pDt
该functor的operator()无视参数,直接返回内部所存储的常数。 e_z"<yq
下面就可以修改holder的operator=了 ^e4y:# Nu
IP-}J$$1
template < typename T > jSMs<ox
assignment < holder, constant_t < T > > operator = ( const T & t) const [X=J]e^D
{ @ 9q/jv`
return assignment < holder, constant_t < T > > ( * this , constant_t < T > (t)); ^iz2=}Q8
} w/Ej>OS
h&Q9
同时也要修改assignment的operator() ;y%C\YB#
HS[N]'dc
template < typename T2 > t]PO4GA
T2 & operator ()(T2 & rhs) const { return l(rhs) = r(rhs); } uU#7SX(uu
现在代码看起来就很一致了。 ]CZ&JL
ZW>?y$C+
六. 问题2:链式操作 vddh 2G
现在让我们来看看如何处理链式操作。 BBUXoz
其实问题1已经为我们处理掉了大量的问题。如果_1,functor,常量彼此之间不统一为functor,那么链式操作的时候就要时刻小心一个对象是_1还是functor还是常量,会大大增加编码的难度。 i=DoK{`L
事实上,首先要解决的是,如何知道一个functor的operator()的返回值的类型。遗憾的是,我并没有找到非常自动的办法,因此我们得让functor自己来告诉我们返回值的类型。 \[F4ooe
比较麻烦的是,operator()的返回值一般和其参数的类型相关,而operator()通常是一个模版函数,因此其返回值类型并不能用一个简单的typedef来指定,而必须实现一个trait。 .pd_SQ~
现在我们在assignment内部声明一个nested-struct L7 f'
`z]MQdE_w
template < typename T > 50J"cGs~
struct result_1 Q?"-[6[v
{ F G5e{
typedef typename ref < typename Left::result_1 < T > ::result > ::reference result; WeqQw?-
} ; :.%Hu9=GL
BR[f{)a5
那么如果参数为T,其返回值类型就为result_1<T>::result。上面代码的ref<T>为一个类型转换类,作用是返回T的引用。不直接加上&符号的原因是如果T本身就是Q的引用Q&,那么Q&&是非法的。因此ref的实现即为: b*@y/ e\u`
?iQA>P9B
template < typename T > A"`(^#a
struct ref .f~x*@
{ q9mYhT/Im
typedef T & reference; FMBzTD
} ; ~IP3~m D
template < typename T > ]'a9>o
struct ref < T &> <+2M,fq+
{ ]&kzIxh
typedef T & reference; _m8JU
} ; 5qW*/
TRSR5D[
有了result_1之后,就可以把operator()改写一下:
l|onH;g\
&:ib>EB03=
template < typename T > \hcb~>=C
typename result_1 < T > ::result operator ()( const T & t) const ;}=[( eqA
{ (HZzA7eph
return l(t) = r(t); V3]"ROH
} C)Ez>~Z
可能大家已经注意到我定义assignment的operator()的返回类型的时候,是直接将其定义为Left的operator()返回类型的引用形式,如果实际上处理的对象的operator=并不是按照常理来声明的,那么这段代码可能就编译不过。这的确是一个很麻烦的事情。实际上,在gcc下,使用typeof关键字可以很容易的得到该类型的operator=的返回类型,就可以让这段代码变得更有通用性。然而为了实现可移植性,我不得不放弃这个诱人的想法。 ?[K\X
同理我们可以给constant_t和holder加上这个result_1。 USrg,A
DTw3$:
有了这个result_1,链式操作就简单多了。现在唯一要做的事情就是让所有的functor都重载各种操作符以产生新的functor。假设我们有add和divide两个类,那么 3%$nRP
X
_1 / 3 + 5会出现的构造方式是: 0W1=9+c|X
_1 / 3调用holder的operator/ 返回一个divide的对象 5lMm8<v
+5 调用divide的对象返回一个add对象。 .5PcprE/
最后的布局是: ixFuqPij
Add &%/kPF~<
/ \ ;v? !Pml2k
Divide 5 nS}XY
/ \ HBc^[fJ^-
_1 3 8}0O @ wq
似乎一切都解决了?不。 ,:!dqonn
你可以想象一下一个完整的Lambda库,它必然能够重载C++几乎所有的操作符。假设其重载了10个操作符,那么至少会有10个代表这些操作符的functor类。大体上来讲,每一种操作符所对应的functor都应当能够由链式操作产生别的任意一种操作符所对应的functor。(例如:*_1 = 2既是由operator*的functor产生operator=的functor)。可想而知这样一共能产生10*10=100种产生方式。这是对编码的一个大挑战。 ]c \gUU
如何简化这个问题呢?我们不妨假定,任意一种操作符的functor,都能够产生任意一种操作符的functor,这样,每一种操作符的functor都拥有一样的产生方案。如果某种转换确实是不合法的(例如:A/B=C无论如何也不可能合法),那么在试图产生新functor的时候会出现编译错误。幸好C++的模版是如果不使用就不编译的,因此这种编译错误不会干扰到正常的使用,这正是我们所要的。 utz!ElzA
OK,我们的方法呼之欲出了。既然所有的functor都具有一样的产生方案,那么不如大家都不要实现,等到最后统一的在所有的functor里面加上这么一系列的产生代码吧。例如,如果要添加从某functor XXX到operator=的functor的产生代码: TLk=HGw
u\-f\Z7
template < typename Right > Jc:gNQCsP
assignment < XXX, typename picker_maker < Right > ::result > operator = ( const tE: m&
;I
Right & rt) const %TA3o71
{ fEl,jA
return assignment < XXX, typename picker_maker < Right > ::result > ( * this , rt); 5$|wW}SA
} }FTyRHD|
下面对该代码的一些细节方面作一些解释 `Al5(0Q
XXX指的是原来的functor的类型,picker_maker<T>是一个类型变换的trait,如果T是一个常量,那么他会返回constant_t<T>,否则返回T本身。 nD$CY K
因此如果该函数声明在assignment的内部,那么就实现了连等,如果声明在的dereference(解引用)的内部,就允许(*A = B)的行为发生。 ?`oCc[hY
最后,如何把这些函数塞到各个functor的声明里边呢?当然可以用宏,但是。。。大家都知道这样不好。 p7A&r:qq#
除了宏之外还可以用的方式就是继承。我们可以写一个类叫做picker,该类实现了所有的如上的产生函数。然后让所有的functor继承自它。 }"'^.FG^_
且慢,也许立刻就有人跳出来说:这样的话那个XXX怎么写呢?这样不是会导致循环依赖么?这样不是会有downcast么? yn[^!GuJ_
正解,让picker做基类确实不是一个好主意。反过来,让picker继承functor却是一个不错的方法。下面是picker的声明: 'b*
yYX<
<R.5Ma
template < class Action > ci@U
a}T
class picker : public Action m-Uq6_e
{ 4oF8F)ASj
public : 3PEv.hGx
picker( const Action & act) : Action(act) {} ZMHb
// all the operator overloaded cIO7RD$8
} ; [7~ !M*o9
JRm:hf'
Picker<T>继承自T,唯一的作用就是给T添加上了各种操作符的重载函数。 s9wcZO
现在所有参与行动的functor都要套上一层picker, _1被声明为 picker<holder>, 并且holder中所重载的操作符除了operator()之外全部被移到了picker内。而picker中的操作符重载的返回的functor也必须套上一个picker: P>dMET
hoc$aqP6pp
template < typename Right > <Cvlz^K[
picker < assignment < Action, typename picker_maker < Right > ::result > > operator = ( const Right & rt) const ueiXY|
{ Q`Q%;%t
return assignment < Action, typename picker_maker < Right > ::result > ( * this , rt); tBp146`
} SY`
U]-h
A(mU,^
Piker_maker返回的也是picker<T>,或者picker<constant_t<T> > "(hhb>V1Wl
使用picker还带来一个额外的好处。之前提到picker_maker要区分functor和常量,有了picker,区分的方法就非常简单了:凡是属于picker<T>的都是functor,否则就是常量。 wnL\.%Y^
0wLu*K5$4E
template < typename T > struct picker_maker d (Fb_
{ D! 1oYr
typedef picker < constant_t < T > > result; E0<9NFQr7
} ; aMSX"N"ot
template < typename T > struct picker_maker < picker < T > > A3p@hQl
{ -$E_L:M
typedef picker < T > result; l)glT]G3+
} ; t]~Lo3
`5[d9z/ 6
下面总的结构就有了: >5&'_
functor专心模拟操作符的行为,并实现一个result_1来告诉别人自己的返回类型。 (Id]'w4
picker专心负责操作符之间的产生关系,由它来联系操作符合functor。 af61!?K
picker<functor>构成了实际参与操作的对象。 3hOiHO
;
至此链式操作完美实现。 DHO6&8S
9=j"kXFf
ZkW,
七. 问题3 a{7>7%[
如何使用多参数的函数对象呢?考虑_1=_2,这个functor必须接受2个参数,因此所产生的assignment对象的operator()必须能接收2个参数。 bUf2uWy7
x.CNDG
template < typename T1, typename T2 > /HsJyp+t
??? operator ()( const T1 & t1, const T2 & t2) const *7Ct#GC
{ +s:!\(BM
return lt(t1, t2) = rt(t1, t2); }@Ij}Ab>
} `/:ZB6
_-&\~w
很明显,这个函数的返回类型会依赖于T1,T2,因此result_1已经无法适用,我们就只好再写一个result_2: ~Cx07I_lf
[lpzUB}<Yp
template < typename T1, typename T2 > fQ5VRpWGn
struct result_2 1nb]~{l
{ l@a>"\><i*
typedef typename ref < typename Left::result_2 < T1, T2 > ::result > ::reference result; :=BFx"Y
} ; Wc4F'}s
Sni Ck*T,
显然,各个functor似乎根本不理会各个参数那个是_1, 那个是_2, 那么最后是怎么选择的呢?
y#5xS
这个差事就留给了holder自己。 XO+^q9
bwG2=
^[noGjy
template < int Order > 1D03Nbh|5
class holder; \`\& G-\
template <> +_tK \MN
class holder < 1 > .eAN`-t;
{ |1zoT|}q
public : G[1:<Vg8
template < typename T > sr+*
q6W
struct result_1 Q#
w`ZQX3
{ \WG6\Zg0A
typedef T & result; |*5K fxq
} ;
C9[Jr)QX
template < typename T1, typename T2 > hPa:>e
struct result_2 ^uIP
{ &13qlc6
typedef T1 & result; k{<]J5{7
} ; f"zXiUV
template < typename T > &v7$*n27
typename result_1 < T > ::result operator ()( const T & r) const xJtblZ1sr
{ :?%$={m
return (T & )r; Hn5:*;N
} ]a)o@FI
template < typename T1, typename T2 > +jePp_3$O
typename result_2 < T1, T2 > ::result operator ()( const T1 & r1, const T2 & r2) const v1Tla]d
{ )$XW~oA'
return (T1 & )r1; ^s/HbCA
} !%{/eQFT4
} ; B#Cb`b"
o(GXv3L
template <> p]/HZS.-b
class holder < 2 > 'M>QA"*48E
{ LeDty_
public : ezn%*X
y,
template < typename T > MaDdiyeC
struct result_1 68
%=
V>V
{ 8"L#5MO t
typedef T & result; 4}@J]_]Z
} ; wQ
/IT}-
template < typename T1, typename T2 > 'thWo wE
struct result_2
n4;
{ ?ACflU_k
typedef T2 & result; +eSNwR=
} ; %UDz4?zx
template < typename T > o2
typename result_1 < T > ::result operator ()( const T & r) const XKD0n^L[
{ h.PVR Awk
return (T & )r; `)Z"||8K
} J jRz<T;
template < typename T1, typename T2 > f%fD>a
typename result_2 < T1, T2 > ::result operator ()( const T1 & r1, const T2 & r2) const `yYo Vu*
{ U.]5UP:a
return (T2 & )r2; JDcc`&`M
} e 4-
} ; #9-qF9M
u~WBu|
Vmq:As^a
新的holder变成了holder<int>, holder<n>的n个参数的operator()会返回第n个参数的值。而_1,_2也相应变为picker<holder<1> >, picker<holder<2> >。 "mlVs/nsyG
现在让我们来看看(_1 = _2)(i. j)是怎么调用的: w U".^
+
首先 assignment::operator(int, int)被调用: 8aDhHXI
s8L=:hiSf)
return l(i, j) = r(i, j); 32nB9[l
先后调用holder<1>::operator()(int, int)和holder<2>::operator()(int, int) a *?bnw?
)Il)
H
return ( int & )i; {j$ :9 H
return ( int & )j;
2P3,\L
最后执行i = j; YJdM6
可见,参数被正确的选择了。 72uARF
iI T7pq1
RCM;k;@8V
1vKAJ<4W
O# n<`;W
八. 中期总结 !C13E lf
目前的结果是这样的,为了支持一个操作符,我们需要作如下几件事: ZfM DyS$.
1。 实现一个functor,该functor的operator()要能执行该操作符的语义 >&pB&'A a
2。 在该functor中实现result_1至result_n,其中n是支持参数的最大值。 }8
V/Cd9
3。 在picker中实现一个操作符重载,返回该functor j#:IG/)GL
/4Ud6gscf
1dDK(RBbQ
]fxYSm
!1G6ZC:z
KrVP#|9%"
九. 简化 og0su
很明显,要支持一个操作符所要做的工作太多了,而且在每个functor中申明result_1至result_n,可见如果n发生变化,维护的开销极大。 =PU!hZj"L
我们现在需要找到一个自动生成这种functor的方法。 `sW+R=
首先,我们注意到result_x的形式很统一。对于各种操作符,其返回值无非下列几种: zt&"K0X|
1. 返回值。如果本身为引用,就去掉引用。 d$DNiJ ,
+-*/&|^等 jQ>~
2. 返回引用。 imb.CYS74
=,各种复合赋值等 okwkMd-yW
3. 返回固定类型。 vndD#/lXq
各种逻辑/比较操作符(返回bool) K
qK?w*Qw
4. 原样返回。 ckDWY<@v
operator, t`F<lOKj
5. 返回解引用的类型。 >|j8j:S[
operator*(单目) ^w|D^F=o
6. 返回地址。 SZ$~zT;c
operator&(单目) 'Cr2&
dy
7. 下表访问返回类型。 ;og[q
operator[] olA 1,8
8. 如果左操作数是一个stream,返回引用,否则返回值 m2sf]-?Y
operator<<和operator>> {Xr|L
"XKcbdr8-
OK,这样我们将返回值类型总结为以上8种,就可以将各种result_x从functor中剥离出来了。 %?2:1o
例如针对第一条,我们实现一个policy类: Q[rmsk2L'
O+f'Ql
template < typename Left > {H F,F=W
struct value_return MBp,!_Q6
{ ~F)[H'$A
template < typename T > :~"Dwrui
struct result_1 O@9<7@h+Nl
{ oItEGJ|
typedef typename const_value < typename Left::template result_1 < T > ::result_type > ::value_type result_type; `1xJ1z#
} ; \US'tF)/
Al93x
template < typename T1, typename T2 > 5vP=Wf cW
struct result_2 YRU1^=v
{ PiL[&_8g
typedef typename const_value < typename Left::template result_2 < T1, T2 > ::result_type > ::value_type result_type; Hl|EySno
} ; w'i8yl
bZ
} ; {OIktG2gZ
{tKi8O^Rb
%[l#S*)~
其中const_value是一个将一个类型转为其非引用形式的trait :,8eM{.Q
E]MyP=g$
下面我们来剥离functor中的operator() K^6fg,&
首先operator里面的代码全是下面的形式: r
&.gOC
]K<mkUpY
return l(t) op r(t) "aT"o
return l(t1, t2) op r(t1, t2) =6YffXa_s
return op l(t) w *Txc}
return op l(t1, t2) [}*xxy
return l(t) op 0?80V'
return l(t1, t2) op ;NoD4*
return l(t)[r(t)] >Hd Pcsl L
return l(t1, t2)[r(t1, t2)] sjW;Nsp
Id}@
很自然的,我们会想到用函数替代这种操作符行为以获得更加一致的形式: 6+.8nx:9X
单目: return f(l(t), r(t)); Jf</83RZ
return f(l(t1, t2), r(t1, t2)); j&y>?Y&Sb
双目: return f(l(t)); wJ>.I<F6B
return f(l(t1, t2)); ^J-"8%
下面就是f的实现,以operator/为例 PSB@yV <
=@\Li)Y
struct meta_divide eVvDis
{ h0c&}kM
template < typename T1, typename T2 > fU^6h`t
static ret execute( const T1 & t1, const T2 & t2) `mp3ORR;$
{ Y I?4e7Z+
return t1 / t2; dN)@/R^E;
} 8GKqPS+
} ; du5|/
u27*-X
5
这个工作可以让宏来做: BpR#3CfW
)4O* D92
#define DECLARE_META_BIN_FUNC(op, desc, ret) struct meta_##desc{\ }"\jB
template < typename T1, typename T2 > \ &Jf67\N
static ret execute( const T1 & t1, const T2 & t2) { return ((T1 & )t1) op ((T2 & )t2);} }; \L5h&