基本用法
lambda表达式是c++最重要也是最常用的特性之一,这是现代编程语言的一个特点,lambda表达式有如下的一些优点:
(1)声明式的编成风格:就地匿名定义目标函数活着函数对象,不需要额外写一个命名函数或者函数对象;
(2)简洁:避免了代码膨胀和功能分散;
(3)在需要的时间和地点实现功能闭包,是程序更加灵活。
lambda表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。lambda表达式的语法形式简答归纳如下:
[capture](params)opt->ret{body;}
capture:变量捕获列表;
params:参数列表,类似函数参数;
opt:函数选项;
ret:返回值类型;
body:函数体;
1.捕获列表[]:捕获一定范围内的变量;
2.参数列表():和普通函数的参数列表一样,如果没有参数列表可以不写。
auto f = [](){return 1};
auto f = []{return 1};
3.opt:不需要时可以省略
- mutable:可以修改按值传递进来的拷贝(注意时能修改拷贝,而不是值本身);
- exception:指定函数抛出的异常,如抛出整数类型的异常,可以使用throw。
4.返回值类型:在c++11中,lambda表达式的返回值是通过返回值后置语法来定义的。
5.函数体:函数的实现,这部分不能省略,但函数体可以为空。
捕获列表
- [] : 不捕捉任何变量;
- [&] : 捕获外部作用域中所有变量,并作为引用在函数体内使用(按引用捕获);
- [=] : 捕获外部作用域中所有变量,并作为副本在函数体内使用(按值捕获);拷贝的副本在匿名函数体内部是只读的;
- [=,&foo] : 按值捕获外部作用域中所有变量,并按照引用捕获外部变量foo;
- [bar] : 按值捕获bar变量,同时不捕获其他变量;
- [&bar] : 按引用捕获bar变量,同时不捕获其他变量;
- [this] : 捕获当前类中的this指针。
让lambda表达式拥有和当前类成员函数同样的访问权限;
如果已经使用了&或者=,默认添加此选项。
类中使用lambda
#include <iostream>
class Test
{
public:
void output(int x, int y)
{
auto x1 = [] {return 1; }; //ok
auto x1 = [] {return m_number}; //error,没有捕获外部变量,因此不能使用m_number
auto x2 = [=] {return m_number + x + y; }; //ok,以值拷贝方式的方式捕获所有外部变量
auto x3 = [this] {return m_number; }; //ok,捕获this指针,可访问对象内部成员
auto x4 = [&] {return m_number + x + y; }; //ok,以引用方式捕获所有外部变量
auto x5 = [this] {return m_number + x + y; }; //error,没有捕获x,y,所以不能使用x,y
auto x6 = [this, x, y] {return m_number + x + y; };//ok,
auto x7 = [this] {return m_number++; }; //ok,正常捕获this指针,并且可以修改对象内部变量的值
}
int m_number = 100;
};
int main()
{
std::cout << "Hello World!\n";
}
函数中使用lambda
int main(void)
{
int a = 10, b = 20;
auto f1 = [] {return a; }; // error,未捕获a
auto f2 = [&] {return a++; }; // ok,引用方式捕获
auto f3 = [=] {return a; }; // ok,值拷贝,只读
auto f4 = [=] {return a++; }; //error,值拷贝,只读,不可以修改
auto f5 = [a] {return a + b; }; // error,未捕获b
auto f6 = [a, &b] {return a + (b++); }; // ok,
auto f7 = [=, &b] {return a + (b++); }; // ok
return 0;
}
注:在匿名函数内部,需要通过lambda表达式的捕获列表控制如何捕获外部变量,以及访问哪些变量。默认状态下lambda表达式无法修改通过复制方式捕获外部变量,如果希望修改这些外部变量,需要通过引用的方式进行捕获。
为什么m_number_1可以修改,m_number_2不可以修改?
返回值
c++中允许省略lambda表达式的返回值类型
// 完整的lambda表达式定义
auto f = [](int a) -> int //int类型可以忽略不写
{
return a+10;
};
// 忽略返回值的lambda表达式定义
auto f = [](int a)
{
return a+10;
};
如果不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值类型,但需要注意的是lambda表达式不能通过列表初始化自动推导出返回值类型。
// ok,可以自动推导出返回值类型
auto f = [](int i)
{
return i;
}
// error,不能推导出返回值类型
auto f1 = []()
{
return {1, 2}; // 基于列表初始化推导返回值,错误
}
函数本质
使用lambda表达式捕获列表捕获外部变量,如果希望去修改按值捕获的外部变量,那么应该如何处理呢?这就需要使用mutable选项,被mutable修改时lambda表达式就算没有参数也要谢敏参数列表,并且可以去掉按值捕获的外部变量的只读(const)属性。
最后再剖析一下,为什么通过值拷贝的方式捕获外部变量是只读的:
1.lambda表达式的类型在c++11中会被看成是带operator()的类,即仿函数;
2.按照c++标准,lambda表达式的operator()默认是const的,一个const成员函数是无法修改成员变量值的。
mutable选项的作用就是取消const属性。