美文网首页
lambda表达式为什么无法再捕获外部变量时转函数指针

lambda表达式为什么无法再捕获外部变量时转函数指针

作者: xiaoliang1 | 来源:发表于2020-07-04 22:44 被阅读0次

    lambda表达式一般是,编译器编译成lambda_xxxxxxxxxx这样类。由于xxxxxxxxxx是随机生成的,所以你一般永远无法事先知道这个类的类名,下面写集中情况来分析。

    1.不捕获外部变量

    int a = 10;
        void (*fun)(int& a);
        [](int& a){
    
            printf("%d", a);
        };
    

    当你这样写时,不捕获外部变量,就相当于外部生成了一个 ’void lambda_xxxxxxxxxx::operator()(int& a)的函数。由于这个类未捕获任何数据,所以类大概是这样的:

    class lambda_xxxxxxxxxx
    {
    public:
        void operator()(int& a);
    
    private:
    
    };
    
    void lambda_xxxxxxxxxx::operator()(int& a) {
        printf("%d", a);
    }
    

    这个类就这样,大小为1字节;
    改写调用时情况:

    int a = 10;
        void (*fun)(int& a);
        [](int& a){
    
            printf("%d", a);
        }(a);
    

    相当于这样:

    lambda_xxxxxxxxxx()(a);
    

    这里直接生成匿名对象,并调用lambda_xxxxxxxxxx::operator()(int& a),很像仿函数吧


    2.捕获外部变量的情况

    下面在说另一种情况:捕获外部变量时:

    int a = 10;
    int b = 30;
    void (*fun)(int& a);
    [&b](int& a){
    
      printf("%d", a);
    };
    

    这个类大概是这样的:

    class lambda_xxxxxxxxxx
    {
    public:
        lambda_xxxxxxxxxx(int& a);
        void operator()(int& a);
    
    private:
        int* b;
    };
    
    lambda_xxxxxxxxxx::lambda_xxxxxxxxxx(int& a) :b(&a) {
    
    }
    void lambda_xxxxxxxxxx::operator()(int& a) {
        printf("%d", a);
    }
    

    这个类的调用过程是:

    int a = 10;
    int b = 30;
    void (*fun)(int& a);
    lambda_xxxxxxxxxx(b);
    

    直接用有参构造生lambda_xxxxxxxxxx::lambda_xxxxxxxxxx(int& a) :b(&a)成一个匿名对象,但未调用lambda_xxxxxxxxxx::operator()(int& a);所以你直接用函数指针去接是不对的,构函数是没有返回值的。所以编译器判定你这个写法有问题;
    在写一个调用的情况:

    int a = 10;
    int b = 30;
    void (*fun)(int& a);
    [&b](int& a){
        printf("%d", a);
    
    }(a);
    

    生成的类是和捕获外部变量,没调用的类一样的;
    调用过程:

    int a = 10;
    int b = 30;
    void (*fun)(int& a);
    lambda_xxxxxxxxxx(b)(a);
    

    这里是先调用有参构造lambda_xxxxxxxxxx::lambda_xxxxxxxxxx(int& a) :b(&a),生成匿名对象,顺手调用了lambda_xxxxxxxxxx::operator()(int& a);

    最后

    当你这样写的

    int a = 10;
    int b = 30;
    void (*fun)(int& a);
    fun = [](int& a){
    
        printf("%d", a);
    };
    

    就相当于

    int a = 10;
    int b = 30;
    void (*fun)(int& a);
    fun = lambda_xxxxxxxxxx::operator()(int& a)
    

    很明显编译认为对的,没有什么语法错误;


    当你捕获时:

    int a = 10;
    int b = 30;
    void (*fun)(int& a);
    fun = [&b](int& a){
    
        printf("%d", a);
    };
    

    相当于:

    int a = 10;
    int b = 30;
    void (*fun)(int& a);
    fun = lambda_xxxxxxxxxx::lambda_xxxxxxxxxx(int& a) :b(&a);
    

    析构没有返回值,此时还产生了一个匿名对象。

    和iOS的block对比,

    虽然两者都很像,但是iOS产生的是一个结构体,基本也算一个类;
    总结下
    相同点:
    1.都是各一个类(虽然block是一个结构体,结构体也可以认为是一个类)
    2.写法很相同
    3.都能捕获外部变量,
    4.都可以通过自己的方法,改写外部变量


    不同点:
    1.lambda是在栈上的匿名对象。block是在堆上的(现在的block都是在堆上的)
    2.lambda无法再捕获外部变量的情况下,用函数指针接,而block可以(runtime自己找)

    总之,block比lambda强大一些
    以上lambda的两种情况都是反汇编得知的

    相关文章

      网友评论

          本文标题:lambda表达式为什么无法再捕获外部变量时转函数指针

          本文链接:https://www.haomeiwen.com/subject/xdmpqktx.html