C++中的lambda表达式

最近学习cocos2d-x的时候经常在触摸事件中看到lambda表达式, 类似于swift中的闭包和Objective-C中的block.


目录

好像好久都没有更新了, (~~强行~~)最近好忙呢【


以下程序使用clang编译, 采用C11标准, 如果读者和我一样使用clang手动编译, 注意加上-stdlib=libc++-std=c++11选项使用C11标准库

函数

lambda表达式其实就是从其他语言中的闭包借鉴来的, 本质上就是匿名的函数. 写程序肯定离不开是用函数和自己写函数. 函数把可以实现一定功能的代码封装到一起, 一方面可以增加代码的复用, 另一方面也可以是程序层次分明, 便于阅读. 为了调用函数, 在定义函数的时候都会定义函数名.

 1
 2
 3
 4
 5
 6
 7
 8
//定义函数名为"func_1"的函数
int func_1(int a) {
    return a;
}

//调用函数
func_1(10);
return 0;

函数调用

除了使用函数名进行函数调用, 为了增加程序的灵活性和实现泛型, C++中也可以使用我们熟悉的函数指针function函数模板来调用函数.

 1
 2
 3
 4
 5
 6
 7
 8
//使用函数指针调用"func_1"
int (*p)(int) = func_1;
(*p)(10);

//使用function模板调用"func_1"
function<int(int)> func;
func = func_1;
fun(10);

熟悉的sort函数

有时候函数写好之后就直接调用, 并且似乎只调用了一次就再也没使用过. 这种情况下, 不定义函数会显得程序结构混乱, 写了函数又感觉没什么必要. 最常见的场景就是sort函数, 基本上所有讲闭包的文章都会提sort函数.

sort函数接受三个参数, beginend分别是需要排序数组的头尾, compare是一个返回值为bool compare(obj1, obj2)的函数, 决定排序的方式(升序或降序, 以及如何比较两个元素大小). sort函数运行时在需要排序的数组中分别取出两个元素作为参数调用compare函数比较, 然后写回原数组.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//定义compare函数
bool compare(int num_1, int num_2) {
    return num_1 < num_ 2;
}

int array[] = {2, 4, 1, 6};

//对array排序
sort(array, array + 3, compare);
//array = (1, 2, 4, 6)

sort函数这样写的确能够增加排序的方式, 不过compare函数与sort函数的调用是分离的, 阅读上会有些困难, 并且compare函数只使用了一次就不再使用. 在这种情况下, 我们就迫切需要一种定义和使用可以在一起, 但又不回过多影响阅读的结构.

lambda表达式

基于上述原因, lambda表达式出现了, 先看一下lambda表达式的格式

[捕获值](参数列表) -> 返回值 {函数体}

捕获值

lambda表达式可以直接使用上文定义的变量, 这个过程叫做捕获, 同参数传递相同, 捕获分为值传递引用传递, 也可以指定只捕获某几个变量.

使用应用传递时, 如果要更改外部变量, 需要在参数列表后加上mutable关键字

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int a = 10;
int b = 100;
//值传递使用a,b, 其实默认[]就是值传递
[=](int num) -> int {
    a += 10;
    b += 100;
    return a + b + num;
};

//引用传递使用a,b
[&](int num)mutable -> int {
    a += 10;
    b +=100;
    return a + b + num;
};

//值传递使用a, 引用传递使用b
[a, &b](int num)mutable -> int {
    a += 10;
    b += 100;
    return a + b + num;
};

//只使用a, 引用传递
[&a](int num)mutable -> int {
    a += 10;
    return a + num;
};

//各个表达式调用结果就不用多说了吧

参数列表、返回值、函数体

和函数相同, 没什么可以多说的…

不过除了捕获值函数体之外, 其他的部分都可以根据情况省略掉, 编译的时候会根据上下文推导出来.

lambda表达式的调用

直接调用

可以把lambda表达式整体看成是函数名, 后面加上参数就可以直接调用

 1
[](int num) -> int {return num} (10);

像函数一样调用

之前也说明了, lambda表达式本质上是匿名函数, 自然能够用调用函数的方法去调用. 对于函数指针, 要求lambda表达式没有捕获外部变量;而对于function模板就无所谓了, 也可以使用auto自动推断.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int a = 10;

//函数指针调用
int (*p) = [=](int num) -> int {
    return a + num;
}; //错误, 函数指针调用的函数不能直接使用外部变量

int (*p2) = [](int num) -> {
    return num;
};
(*p_2)(10);//正确调用

//function模板调用
function<int(int)>func;
func = [=](int num) -> int {
    return a + num;
};
func(10);

//auto自动判断lambda表达式类型
auto func_2 = [=](int num) -> int {
    return a + num;
};
func_2(10);

再回到sort函数

知道了lambda表达式的使用之后就可以解决之前在sort函数中遇到的问题了

 1
 2
 3
 4
int array[] = {2, 4, 1, 6};
sort(array, array + 3, [](int num_1, int num_2) -> bool {
    return num_1 < num_2;
});

是不是比刚才简洁不少, 阅读器来也不是那么困难→_→(如果想swift一样可以尾随闭包就更好了)

除了向sort函数或者各种交互事件等需要回调函数的场景, 我们在写函数的时候也可以试着使用lambda表达式, 比如利用lambda表达式传值之类的, 简化代码.


emmmm, 上次更新服务器系统时忘了备份数据库, 再加上评论比较少, 暂时关闭评论功能