• C++中lambda的实现(1)


    在看C++ Primer的过程中,发现C++11标准中添加了lambda和类型推断系统。这篇文章介绍了很多lambda的实例

    为了弄清楚lambda的实现,特地做了一个小实验。这一次只看non-mutable lambda。测试的gcc版本为4.6.3(貌似4.5以前的gcc不支持lambda表达式)。

    代码:

     1 //test.cpp
     2 #include <iostream>
     3 template<typename Func>
     4 void test(Func f){
     5     std::cout<<f(3)<<std::endl;
     6 }
     7 template<typename Func>
     8 void test2(Func f){
     9     std::cout<<f(13)<<std::endl;
    10 }
    11 int main(){
    12     int a = 1;
    13     int b = 2;
    14     auto f = [a,b](int c){return a+b+c;};
    15     test(f);
    16     a=2;b=3;
    17     test2(f);
    18     return 0;
    19 }

    编译方法

    g++ -std=c++0x test.cpp -o test

    运行结果

    ./test
    6
    16

    在这个例子中,代码第14行创建了一个non-mutable lambda。这里使用了类型自动推断,f的类型编译器会自动计算出来。[]称为capture list,里面的a和b的值同main里面的a和b的值是一样的,而且可以发现non-mutable lambda创建之后再对main中的a、b赋值,不会影响到capture list中的a和b的值。此外capture list中a和b是只读的,试图写这两个值时,编译器会报错。这个lamda函数的返回值类型是int,这也可以由编译器自动推断出来。

    现在来看看生成的汇编代码,来看看C++是如何实现non-mutable lambda的。

    下面这一段是从main函数的汇编代码中截取出来的,对应了C++代码第12行到15行。各行的意义参见注释。

     1 8048612:       c7 44 24 18 01 00 00    movl   $0x1,0x18(%esp)  #给main中的a赋值
     2  8048619:       00 
     3  804861a:       c7 44 24 1c 02 00 00    movl   $0x2,0x1c(%esp) #给main中的b赋值
     4  8048621:       00 
     5  8048622:       8b 44 24 18             mov    0x18(%esp),%eax 
     6  8048626:       89 44 24 10             mov    %eax,0x10(%esp) #将main中的a值copy到capture list中的a(记为c_a)
     7  804862a:       8b 44 24 1c             mov    0x1c(%esp),%eax
     8  804862e:       89 44 24 14             mov    %eax,0x14(%esp) #将main中的b值copy到capture list中的b (记为c_b)
     9  8048632:       8b 44 24 10             mov    0x10(%esp),%eax 
    10  8048636:       8b 54 24 14             mov    0x14(%esp),%edx
    11  804863a:       89 04 24                mov    %eax,(%esp)     #%eax中放着c_a的值
    12  804863d:       89 54 24 04             mov    %edx,0x4(%esp)  #将capture list中a、b的值分别取出,放在栈上作为函数调用的参数
                                           #(分别记为c_a_copy1, c_b_copy1)
    13 8048641: e8 2b 00 00 00 call 8048671 <_Z4testIZ4mainEUliE_EvT_> #调用test(f)

    下面这一段是从函数模版test的汇编代码中截取的一段,对应了test中的f(3)这个表达式。

    1  8048677:       c7 44 24 04 03 00 00    movl   $0x3,0x4(%esp)   #将3作为参数放在栈上
    2  804867e:       00 
    3  804867f:       8d 45 08                lea    0x8(%ebp),%eax   #0x8(%ebp)对应的地址就是c_a_copy1
    4  8048682:       89 04 24                mov    %eax,(%esp)      #将这个地址(c_a_copy1的地址)放在栈上
    5  8048685:       e8 6a ff ff ff          call   80485f4 <_ZZ4mainENKUliE_clEi> #调用lambda的代码

    下面是lambda代码对应的汇编码

    1 80485f7:       8b 45 08                mov    0x8(%ebp),%eax    #将c_a_copy1的地址取出
    2  80485fa:       8b 10                   mov    (%eax),%edx    #%edx中存放c_a_copy1的值
    3  80485fc:       8b 45 08                mov    0x8(%ebp),%eax
    4  80485ff:       8b 40 04                mov    0x4(%eax),%eax   #%eax中存放着c_b_copy1的值
    5  8048602:       01 d0                   add    %edx,%eax
    6  8048604:       03 45 0c                add    0xc(%ebp),%eax  #0xc(%ebp)中放着参数c的值,着两行对应a+b+c

    从上面的代码来看,C++在编译lambda的时将lambda作为一个特殊的函数来处理。non-mutable lambda编译出来的代码与函数的代码类似,capture list中的值是存放在创建者(本例中是main函数)的栈上的。使用non-mutable lambda的时候,capture list中的值被拷贝出来放在调用栈上,在lambda函数中取出来使用。

    下面再来看一下main中的另一段汇编代码,这一段代码对应着C++中的16和17行。

    1  8048646:       c7 44 24 18 02 00 00    movl   $0x2,0x18(%esp)   #给a赋值为2,没有影响c_a的值
    2  804864d:       00 
    3  804864e:       c7 44 24 1c 03 00 00    movl   $0x3,0x1c(%esp)   #给b赋值为3,没有影响到c_b的值
    4  8048655:       00 
    5  8048656:       8b 44 24 10             mov    0x10(%esp),%eax
    6  804865a:       8b 54 24 14             mov    0x14(%esp),%edx
    7  804865e:       89 04 24                mov    %eax,(%esp)
    8  8048661:       89 54 24 04             mov    %edx,0x4(%esp)    #仍然是从c_a和c_b中拷贝值放在栈上
    9  8048665:       e8 42 00 00 00          call   80486ac <_Z5test2IZ4mainEUliE_EvT_> #调用test2(f),后面部分的原理与test(f)相似

    从这段代码我们可以看出,capture list中的变量的值使用的是non-mutable lambda创建的时刻的值,以后不随着capture list外的值的变化而变化。

    这篇文章分析了一下mutable lambda的实现

  • 相关阅读:
    react.js 你应知道的9件事
    table的border-collapse属性与border-spacing属性
    深入理解 CSS变形 transform(3d)
    $ 的绑定事件
    保留两位小数
    数据库日期格式化
    javaScript对两个数组进行去重
    js中的原型链__proto__其实超简单!!
    JSON.parse()和JSON.stringify()应用理解
    Java Web 重归
  • 原文地址:https://www.cnblogs.com/richardustc/p/2964736.html
Copyright © 2020-2023  润新知