一 lambda用法简介
C++ 11引入
也是一种可调用对象
lambda表达式,它定义了一个匿名函数,并且可以捕获一定范围内的变量
基本形式 以及解释
auto 返回类型后置 语法。
void main() {
//lambda表达式的基本形式
auto f = [](double dou)->int {
cout << "dou*2 = " << dou*2 << endl;
return dou * 2;
};
//说明:lambda表达式是一个可调用对象
//f是返回值
//[]是捕获符,可以从外界捕获想要的值,例如想要使用外界的变量,需要在[]里面写东西
//()里面是参数,表示该lambda表达式需要的参数
//->int,表示该lambda表达式的返回值
//{}里面是函数体
//;记得最后加上这个分号
//调用
int res = f(69.9);
cout << "res = " <<res << endl;
//结果
// dou * 2 = 139.8
// res = 139
}
其中:-> int 可以省略,lambda表达式会根据 真正的返回值决定返回啥类型
auto f1 = [](double dou) {
cout << "dou*2 = " << dou * 2 << endl;
return dou * 2;
};
cout << f1(3.14) << endl;
//结果
//dou*2 = 6.28
//6.28
特点:
a 是一个匿名函数,也可以理解为:可调用的代码单元。
b 它有一个返回类型,一个参数列表,一个函数体。
c 与函数不同的是:lambda表达式可以在函数内部定义,常规函数不行。例如上述代码中,我们是在main函数中写的lambda表达式。
d.一般形式说明
//f是返回值
//[]是捕获列表,可以从外界捕获想要的值,例如想要使用外界的变量,需要在[]里面写东西
//()里面是参数,表示该lambda表达式需要的参数
//->int,表示该lambda表达式的返回值
//{}里面是函数体
//;记得最后加上这个分号
f.因为很多时候,lambda表达式返回值特别明显,所以允许lambda表达式返回类型 可以省略。编译器可以自动推导。
g.()中的参数可以给默认值,如果给了默认值,则调用的时候可以省略参数
auto f2 = [](double dou = 90.8) {
cout << "dou*2 = " << dou * 2 << endl;
return dou * 2;
};
cout << f2() << endl;
h 编译器并不是总能推断出 返回值类型,如果编译器推断不出来返回值类型的时候,必须显示的给出返回值类型。
i 在没有参数的的时候,小括号也可以省略。
auto f3 = []() {
cout << "f3 called" << endl;
};
f3();
auto f4 = [] {
cout << "f4 called " << endl;
};
f4();
j 捕获列表[] 和 函数体{} 必须包含。
k lambda 表达式调用和 普通函数相同
m lambda表达式 可以没有返回值
二。捕获列表
就是[]里面的东西。
通过捕获列表来捕获外边一定范围内的变量。
1. [] ---空的中括号 表示不捕获任何变量
2. 静态变量不需要捕获。
注意的是:如下的代码,局部变量在没有捕获的情况下,是不能使用的,会有build error
但是,如果是静态局部变量 或者 全局的静态变量,则可以访问,注意mage 和mage2的区别。
static int magequanju = 980;
void main(){
int mage = 9;
static int mage2 = 10;
auto f5 = [] {
mage2 = 100;//ok
magequanju =1101010;//ok
mage = 90;//如果不在[]中捕获外边的变量,则这一行会有build error
cout << "f5 called " << endl;
};
f5();
}
3.[&] 捕获外部作用域中所有的变量,并 作为引用 在函数体内使用
int mage = 10;
auto f6 = [&] {
mage2 = 100;//ok
mage3 = 890;
mage = 90;//中括号中的& 表示 捕获外部作用域的所有变量,并作为引用在函数体内使用。
cout << "f5 called " << endl;
};
f6();
cout << "mage = " << mage << endl;//结果为90
这里有两个问题需要注意:
1.改动的是引用,因此在lambda表达式中的改动该值后,外边也会看到这个值变化了
2.要注意作用域,mage的作用域是在main函数内部,但是我们这个lambda表达式的调用,也就是f6(),实际上有可能是什么时候调用的,因此要特别注意在调用的时候mage的作用域
4. [=] 捕获外部作用域中的所有变量,在函数中使用,也就是可以用它的值,但是不能给赋值。
auto f7 = [=] {
//mage = 11190;//build error 中括号中的= 表示 只能用,不能改。
int res = mage + 80;
cout << "f7 called res = " << res << endl;
};
f7();
cout << "mage = " << mage << endl;//结果为90
//f7 called res = 170
// mage = 90
5. [this],一般用于类中,捕获当前类中this指针,让lambda表达式有和当前类成员函数同样的访问权限,如果[]中已经使用了 & 或者 =,那么默认就已经使用了this。说白了,捕获this的目的就是为了在lambda中使用当前类的成员函数和成员变量。
class Teacher130 {
public:
int mage = 90;
void myfuncpt(int x, int y) {
//这里不管是this ,还是 & 还是 = 都可以达到访问类成员mage
auto mylambda = [this]() {
return mage;
};
cout << mylambda() << endl;
}
};
void main() {
Teacher130 tea;
tea.myfuncpt(10,20); //结果:90
}
6. []里面是变量名,如果是多个变量名,彼此之间用,分隔。代表的含义是按值捕获 中括号里面的变量名,不捕获不在中括号中的变量。只能访问,不能改动。
那么在 []中写= 号不是更好吗?为啥我们还要一个一个写。
这是因为如果是=号,lambda表达式会将所有的变量都存储一份,这个资源消耗较大,如果程序很大,那消耗的就更大了,而使用在[]中写变量名的方式更加合理。消耗更少。
void main() {
int mage = 80;
string str("nihao");
auto f1 = [mage,str](double dou) {
cout << "mage = " << mage << endl;
cout << "str = " << str << endl;
//mage = 8;//build error ,只能访问,不能修改
cout << "dou*2 = " << dou * 2 << endl;
return dou * 2;
};
f1(89);
}
7 []里面是&变量名,如果是多个变量名,彼此之间用,分隔。代表的含义是按引用捕获 中括号里面的变量名,不捕获不在中括号中的变量。可以访问,可以改动。
auto f2 = [&mage, &str](double dou) {
mage = 8;//可以访问,可以修改
str = "nishuo";
cout << "dou*2 = " << dou * 2 << endl;
cout << "mage = " << mage << endl;
cout << "str = " << str << endl;
return dou * 2;
};
f2(189);
8. [=,&变量名],按值捕获所有的变量,按引用 捕获变量名,打头的=表示默认捕获方式
auto f3 = [=, &str](double dou) {
//mage = 8;//不可修改
str = "nishuo";
cout << "dou*2 = " << dou * 2 << endl;
cout << "mage = " << mage << endl;
cout << "str = " << str << endl;
return dou * 2;
};
f3(1189);
9.[&,变量名],按引用捕获所有的变量,按值 捕获变量名,打头的&表示默认捕获方式
auto f4 = [&, str](double dou) {
mage = 8;//可以修改
//str = "nishuo"; // 不可修改
cout << "dou*2 = " << dou * 2 << endl;
cout << "mage = " << mage << endl;
cout << "str = " << str << endl;
return dou * 2;
};
f4(11189);
三。lambda表达式延迟调用易出错细节分析
如果是用 = 捕获的,或者用变量名捕获的,也就是值捕获,那么在lambda表达式赋值的时刻,就会将所有的变量复制一份。因此会有如下问题:
问题代码
int xx = 10;
auto f5 = [=]() {
return xx;
};
xx = 8080;
cout << f5() << endl; //结果是10
int xxxx = 1000;
auto f7 = [xxxx]() {
return xxxx;
};
xxxx = 808090;
cout << f7() << endl; //结果是1000
fix 方案:使用引用解决
int xxx = 10;
auto f6 = [&]() {
return xx;
};
xxx = 8080;
cout << f6() << endl; //结果是8080
四,lambda表达式中的,mutable
//回忆一下 mutable (不容易,不稳定)修饰成员变量,用于const 的反义词。
//如果该成员变量被 mutable 修饰,表示即使函数是const ,也可以改动该值
//假设我们这里是 值传递的,但是又想在内部改动该值,也可以使用修饰符mutable来达到这个目的。
注意的是:这个改动不会真正的改动 y 的值,改动的只是 y 的副本
//回忆一下 mutable (不容易,不稳定)修饰成员变量,用于const 的反义词。
//如果该成员变量被 mutable 修饰,表示即使函数是const ,也可以改动该值
//假设我们这里是 值传递的,但是又想改动该值,也可以使用修饰符mutable来达到这个目的。
int y = 1000;
auto f8 = [y]()mutable {
y = 9000;
return y*2;
};
cout << f8() << endl; //结果是18000
cout << y << endl; //结果是1000
五 lambda表达式对的类型以及 存储
在C++11中,lambda表达式的类型被称呼为 “闭包类型”。
闭包:可以理解为函数内的函数,也是可调用对象,本质上就是lambda表达式创建的运行时期的对象。
lambda表达式是一种比较特殊的,匿名的,类类型【闭包类】的对象(也就是定义一个类型,又生成一个匿名的该类 类型的对象)。
我们可以认为 它是一个带有 operator()的类类型对象。也就是仿函数。
所以,我们也可以用 std::function 和 std::bind来保存和调用 lambda表达式。
每个lambda表达式都会触发编译器生成一个 独一无二的类类型,以及返回给我们一个这个类类型的对象。
lambda表达式这种语法,让我们可以就地定义匿名函数(就地封装短小的功能闭包)
void main() {
//使用function 接受 lambda的返回值,并调用
std::function<int(int)> f1 = [](int vt) {
return vt * 2;
};
cout << f1(8880) << endl;
//使用bind 将 lambda表达式和参数组合。bind 的作用是1.能够将对象以及相关的参数绑定在一起,绑定完后可以直接调用。
auto b2 = bind([](int vt) {return vt * 7; }, std::placeholders::_1);
std::function<int(int)> f2 = b2;
cout << f2(888888) << endl;
//结果:17760
// 6222216
}
语法糖概念
int a [5];
a[1] = 90;// 这个就是语法糖。
实际写法应该是: *(a+1) = 90;
语法糖的意思是:让程序员写代码更加方便。
所以 lambda表达式,可以看成是顶一个仿函数闭包(函数中的函数)的语法糖。
六 不捕获任何变量的lambda表达式,也就是捕获列表为空,可以转换成一个普通的函数指针。
using functype = int(*)(int);
functype fp = [](int ve) {return ve * 2; };
cout << fp(890) << endl;
七 lambda 表达式再演示和优点聪姐
for_each简介
是一个函数模版,
需要添加 #include <algorithm>
一般是用来配合函数对象使用的
void func132(int value) {
cout << "value = " << value << endl;
}
void main() {
//for_each的使用
vector<int> vec = {10,20,30,40,50,60};
for_each(vec.begin(),vec.end(),func132);
//for_each 结合 lambda表达式的使用
int sum = 0;
for_each(vec.begin(), vec.end(), [&sum](int value) {
sum = sum + value;
});
cout << "sum = " << sum << endl;
}
find_if简介
函数模版,用来查找一个东西,查找什么,取决于第三个参数
find_if的第三个参数只要返回false,就会不停的遍历 vector,直到查找到返回 true为止。或者遍历完毕后没有找到。
//find_if 返回值:范围[first, last) 中首个满足以下条件的迭代器 it,或者在没有满足条件的迭代器时返回 last
auto result = find_if(vec.begin(), vec.end(), [](int value) {
if (value %2 ==0 ) {
return true;
}
return false;
});
cout << "result = " << *result << endl;
本文详细介绍了C++11中的lambda表达式,包括其基本形式、返回类型、参数列表、捕获列表的使用,以及与普通函数的差异。还探讨了lambda表达式的延迟调用问题及其解决方案,以及lambda表达式的类型存储和语法糖概念。
1598

被折叠的 条评论
为什么被折叠?



