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

2020-07-04  本文已影响0人  xiaoliang1

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的两种情况都是反汇编得知的

上一篇下一篇

猜你喜欢

热点阅读