C++中lambda的实现(2)
之前写了一篇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 }
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
./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对应的函数
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
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实现函数。