之前写了一篇C++中的lambda的实现(1),从汇编语言的角度来分析了一下non-mutable lambda的实现方式。这篇文章主要介绍一下mutable lambda的实现方式。而这篇文章中有比较详细的lambda语法示例。
实验用gcc版本4.7.2,据说4.5以前的gcc不能支持C++11中的lambda。
C++源码
1 //lambda.cpp 2 #include <iostream> 3 using namespace std; 4 int main(){ 5 int a,b; 6 a = 1; 7 b = 2; 8 auto f = [&a, &b](int c)->int{ a++; b++; return a+b+c;}; //创建一个lambda表达式,'&'表示a, b以引用传递,
//可以被修改,并且修改会反映到main中的a、b 9 cout<<"result= "<<f(3)<<endl; 10 cout<<"a= "<<a<<endl; 11 cout<<"b= "<<b<<endl; 12 return 0; 13 }
编译运行
g++ -std=c++0x lambda.cpp -o lambda ./lambda result= 8 a= 2 b= 3
从结果来看,lamba表达式执行之后,a、b的值成功地被修改了。
下面这一段是main函数6-8行以及f(3)对应的汇编代码,同上次分析的一样,lambda在C++中编译成一个函数。
1 80487ea: c7 44 24 10 01 00 00 movl $0x1,0x10(%esp) # a=1 2 80487f1: 00 3 80487f2: c7 44 24 14 02 00 00 movl $0x2,0x14(%esp) # b=2 4 80487f9: 00 5 80487fa: 8d 44 24 10 lea 0x10(%esp),%eax 6 80487fe: 89 44 24 18 mov %eax,0x18(%esp) #0x18(%ebp)(记为ref_a)中存放了a的地址 7 8048802: 8d 44 24 14 lea 0x14(%esp),%eax 8 8048806: 89 44 24 1c mov %eax,0x1c(%esp) #0x1c(%ebp)(记为ref_b)中存放了b的地址 9 804880a: c7 44 24 04 03 00 00 movl $0x3,0x4(%esp) #将f(3)中的参数3放在栈上 10 8048811: 00 11 8048812: 8d 44 24 18 lea 0x18(%esp),%eax 12 8048816: 89 04 24 mov %eax,(%esp) #将ref_a的地址放在栈上 13 8048819: e8 8e ff ff ff call 80487ac <_ZZ4mainENKUliE_clEi> #调用lambda对应的函数
下面这一段是lambda函数所对应的汇编代码
80487ac: 55 push %ebp 80487ad: 89 e5 mov %esp,%ebp 80487af: 8b 45 08 mov 0x8(%ebp),%eax #取出上面一段汇编代码中12行所存的ref_a的地址 80487b2: 8b 00 mov (%eax),%eax #取出ref_a的值,也就是a的地址 80487b4: 8b 10 mov (%eax),%edx #取出a的值到edx 80487b6: 83 c2 01 add $0x1,%edx # a++ 80487b9: 89 10 mov %edx,(%eax) #存回a,注意这个时候是存回到了main函数栈上的a里面 80487bb: 8b 45 08 mov 0x8(%ebp),%eax #取出ref_a的地址 80487be: 8b 40 04 mov 0x4(%eax),%eax #ref_a和ref_b是紧挨着存储的
#这一条指令取出ref_b的值,也就是b的地址 80487c1: 8b 10 mov (%eax),%edx #取出b的值,放入edx 80487c3: 83 c2 01 add $0x1,%edx # b++ 80487c6: 89 10 mov %edx,(%eax) #存回b 80487c8: 8b 45 08 mov 0x8(%ebp),%eax 80487cb: 8b 00 mov (%eax),%eax 80487cd: 8b 10 mov (%eax),%edx #再取a的值,放在edx 80487cf: 8b 45 08 mov 0x8(%ebp),%eax 80487d2: 8b 40 04 mov 0x4(%eax),%eax 80487d5: 8b 00 mov (%eax),%eax 80487d7: 01 c2 add %eax,%edx #edx = a+b 80487d9: 8b 45 0c mov 0xc(%ebp),%eax #取出上一段汇编中第9行放在站上的参数3 80487dc: 01 d0 add %edx,%eax #eax= a+b+c,而函数返回值放在eax中 80487de: 5d pop %ebp 80487df: c3 ret
从上面的汇编分析来看,gcc在实现mutable lambda的时候,将lambda的代码编译为函数,将capture list中的可写变量的地址通过一些手段传入到lambda实现函数中。对比之下,non-mutable的lambda则将capture list中变量的值复制一遍,再将复制出来的变量的地址通过一些手段传入lambda实现函数。