相信使用过python的人都知道lambda函数,一种简单的无需定义标识符即函数名的函数或者子程序。C++11新标准引入后,也同样的添加了Lambda函数,下面来具体介绍C++匿名函数的概念和使用。
Lambda函数
Lambda表达式的声明:
[
c
a
p
t
u
r
e
l
i
s
t
]
(
p
a
r
a
m
s
l
i
s
t
)
m
u
t
a
b
l
e
e
x
c
e
p
t
i
o
n
→
r
e
t
u
r
n
t
y
p
e
{
f
u
n
c
t
i
o
n
b
o
d
y
}
[capture\quad list] (params\quad list) mutable\quad exception\rightarrow return\quad type \{ function\quad body \}
[capturelist](paramslist)mutableexception→returntype{functionbody}
各参数具体解释:
- capture list: 捕获外部变量列表ss
- params list: 形参列表
- mutable(指示符): 是否可以修改捕获的变量。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(参数为空)
- exception: 异常设定
- return type: 返回类型,可以不需要声明返回值(连同 → \rightarrow →一起省略),此时返回类型相当于使用decltyp根据返回值推断得到。
- function body: 函数体
使用格式
-
[
c
a
p
t
u
r
e
l
i
s
t
]
(
p
a
r
a
m
s
l
i
s
t
)
→
r
e
t
u
r
n
t
y
p
e
{
f
u
n
c
t
i
o
n
b
o
d
y
}
[capture\quad list] (params\quad list)\rightarrow return\quad type \{ function\quad body \}
[capturelist](paramslist)→returntype{functionbody}
注:省略mutable和异常,默认为const类型表达式,不可改变捕获列表中的值。 -
[
c
a
p
t
u
r
e
l
i
s
t
]
(
p
a
r
a
m
s
l
i
s
t
)
{
f
u
n
c
t
i
o
n
b
o
d
y
}
[capture\quad list] (params\quad list)\{ function\quad body \}
[capturelist](paramslist){functionbody}
注:省略返回值类型,可以通过函数定义的主体部分return来确定返回值类型,没有return则为void类型。 -
[
c
a
p
t
u
r
e
l
i
s
t
]
{
f
u
n
c
t
i
o
n
b
o
d
y
}
[capture\quad list]\{ function\quad body \}
[capturelist]{functionbody}
注:省略了形参列表,即函数为无参函数。
捕获外部变量形式
捕获形式 | 说明 |
---|---|
[] | 不捕获任何外部变量 |
[变量名,…] | 默认以值的形式捕获制定的外部变量(逗号分割) |
[this] | 以值的形式捕获this指针 |
[=] | 以值的形式捕获所有外部变量 |
[&] | 以引用的形式捕获所有外部变量 |
[&, a] | 以值的形式捕获外部变量a,以引用的形式捕获其他外部变量 |
[=, &a] | 以引用的形式捕获外部变量a,以值的形式捕获其他外部变量 |
形参注意点
参数列表和普通函数的参数类似,但是存在一些注意点。
- 参数列表中不能有默认参数
- 不支持可变参数
- 所有参数必须有参数名
比较函数指针、函数符和Lambda函数
使用一个实例来进行三种类型的比较。
假设面试中遇到测试代码快速实现的小例子中,面试官要求你生成一个随机整数列表,并判断其中有多少个整数可以被3整除,多少个被13整除。
首先生成这样的列表很简单,可以使用vector存储,使用STL算法generate()在其中填充随机数。
#include<vector>
#include<algorithm>
#include<cmath>
...
std::vector<int> numbers(1000);
std::generate(numbers.begin(), numbers.end(), std::rand);
以上通过一个指向标准函数rand()的指针随机产生整数填充到numbers里面。之后通过count_if()函数来计算符合条件的元素有多少个。
为了判断是否能被3、13整除,可以定义一下函数:
bool f3(int x) { return x % 3 == 0;}
bool f13(int x) { return x % 13 == 0;}
定义完以上的准备函数后,可以计算符合条件的元素个数了。
int count3 = std::count_if(numbers.begin(), numbers.end(), f3);
cout << "Count of numbers divisible by 3: " << count3 << endl;
int count13 = std::count_if(numbers.begin(), numbers.end(), f13);
cout << "Count of numbers divisible by 13: " << count13 << endl;
之后使用函数符来完成这个任务。
函数符:一个函数对象,可以以函数方式与()结合使用的任意对象,包括函数名、指向函数的指针和重载了()运算符的类对象(即定义了函数operator()()的类)。就这个实例而言,函数符的优点之一:可使用同一个函数符来完成两项计数任务。
class f_mod
{
private:
int value;
public:
f_mod(int d = 1) :value(d) {}
bool operator()(int x) { return x % value == 0; }
};
可以使用构造函数创建存储特点整数的f_mod对象。
f_mod obj(3); // 设置被3整除
bool flag = obj(7); // 使用方法operator()来判断7是否能被3整除
// 同理被13整除
f_mod obj(13);
bool flag = obj(7);
最后使用Lambda的情况。
int count3 = std::count_if(numbers.begin(), numbers.end(), [] {int x} {return x % 3 == 0; });
int count13 = std::count_if(numbers.begin(), numbers.end(), [] {int x} {return x % 13 == 0; });
此时就是没有指定返回类型,通过return来进行判断返回类型为bool。
为何使用Lambda函数
也许可能觉得除了表达式狂热爱好者,谁会使用这个呢?其实C++11新引入的,以及很多语言如:python、C#都有此类使用。还是那句话“存在即有理”。下面将从四个方面来讨论为何使用Lambda。
- 距离
很多人认为,让定义位于使用的地方附近很有用。这样,就无需翻阅很多页的源代码,以了解函数。另外,如果需要修改代码,设计的内容就在附近,就很好修改。lambda是不错的选择。而函数是不好的选择,因为不能内部定义其他函数,因此定义可能离使用的地方很远。函数符是个不错的选择,可以在函数内部定义类(包含函数符类)。 - 简洁
函数符代码要比lambda代码更加繁琐,函数和lambda的简洁程度相当。 - 效率
三种方法相对效率取决于编译器内联的那些东西。函数指针阻止了内联,因为编译器传统上不会内联其他地址被获取的函数,因为函数地址的概念一位着非内联函数。而函数符和lambda通常不会阻止内联。 - 功能
lambda可以访问作用域内的任何动态变量,可以采用取值、引用的形式进行捕获。
总结
C++中引入lambda的主要目的是:让我们可以将类似于函数的表达式用作接受函数指针或者函数符的函数参数。因此,典型的lambda是测试表达式或者比较表达式,可编写为一条返回语句。(注:可以使用变量来接受lambda函数,作为函数指针的效果。其类型使用auto来接受。
eg:auto f3 = [](int x) {return x % 3 == 0; };
)。
参考资料
C++ Primer Plus