当前位置: 首页 > news >正文

河西集团网站建设自媒体十大平台

河西集团网站建设,自媒体十大平台,云南省文山建设厅网站,网站模板 扁平化得益于C11的发布,提供了提高效率的语法,C11以后是现代C,C98是传统C,这里来介绍lambda的使用和原理。 目录 1.lambda 2.列表捕捉 3,lambda的应用 4,lambda原理 1.lambda lambda表达式本质是一个匿名函…

得益于C++11的发布,提供了提高效率的语法,C++11以后是现代C++,C++98是传统C++,这里来介绍lambda的使用和原理。

目录

1.lambda

2.列表捕捉

3,lambda的应用

4,lambda原理


1.lambda

lambda表达式本质是一个匿名函数对象,跟普通函数不一样,它们可以定义在函数内部。

lambda使用层而言它们没有类型。是编译器自动去分配的,所以我们用auto去接受lambda对象。

什么意思嘞?就是我们不知道lambda的返回类型,所以我们用auto让编译器自己去推到。

	auto pd = [](int a, int b)->int{ return a + b; };cout << pd(5, 3);

lambda表达形式,

//[]捕捉列表()参数列表->返回类型{}函数体;

[]捕捉列表不能省略,参数列表无参数可省略,返回类型明确,函数体不可以省略。

[]捕捉列表,该表总是出现在lambda开始的位置,编译器根据[]判断下列的函数是否为lambda函数,捕捉列表能够捕捉上下文的变量供lambda使用,分为传值捕捉,传引用捕捉。捕捉列表为空也不可以省略。为啥要捕捉呢?这是因为作用域的限制,普通函数想使用main函数中定义的参数必须进行传参,因为他们的作用域不同,lambda也是函数,如果不传参它不能使用函数作用域外的参数,所以用捕捉列表来捕捉供他使用。

()参数列表,和普通函数传参类似,如果不需要可以省略。

->返回类型,函数返回什么类型就写什么类型。

{}函数体,和普通函数类似,在函数体内除了可以使用参数,还可以使用所有捕捉到的变量,函数体为空也不能省略。

#include<iostream>
using namespace std;
int main()
{auto pd = [](int a, int b)->int{ return a + b; };//[]捕捉列表()参数列表->返回类型{}函数体;cout << pd(5, 3);return 0;
}

2.列表捕捉

lambda函数中我们了解[]是捕捉列表,里面是捕捉的参数,我们还提到分为传引用捕捉和传值捕捉。

lambda函数只能使用lambda函数体中的变量和参数,如果想使用外界的其它变量就需要用捕捉列表进行捕捉。

第一种捕捉方式是在[]中进行显示的传值捕捉和传引用捕捉,捕捉的多个变量用,分割。

[x,&y,z]xz是传值捕捉,传值捕捉的值默认被const修饰不能修改,但是它和外界的a是两份变量地址不一样,传引用捕捉的值可以被修改,修改传引用的值外界的变量也会被修改。

我们用代码来验证上面所说的两份a和传引用捕捉被修改。


int main()
{int c = 5;int d = 10;cout << "被修改前的d" << d<<endl;auto pd = [c, &d](int a, int b)->int {cout << "函数体c地址:" << &c << endl;; d--; return a + b; };//[]捕捉列表()参数列表->返回类型{}函数体;cout << pd(5, 3)<<endl;cout << "外界的c地址" << &c << endl;cout << "被修改后的d" << d;return 0;
}

我们可以看到传值捕捉的c和外界的c地址不一样,说明它两是两份a,类似的,传引用捕捉的b当它在函数体内进行d--后它在函数体外的d也被修改,说明它两的地址一样,同命相连。

第二种捕捉方式是隐式捕捉,这样[=]是传值捕捉,[&]是传引用隐式捕捉,区别是它会自动捕捉所有在函数体中用到的值,传值隐式捕捉不能被修改,传引用隐式捕捉可以被修改,意思是我们在lambda里用了哪些变量,它就会捕捉哪些变量。比如:

我们在函数体没有定义a,但是隐式捕捉之后它会返回5,所以我们看到它会自动捕捉函数体内用到的值。

int a = 5;
auto pd = [=]()->int { return a; };
cout << pd();

当我们修改代码后,在函数体内也创建a,它会返回函数体的a,这是就近原则。

第三种捕捉方式是混合捕捉,即隐式捕捉和显示捕捉一起用,[=,&x]这个的意思是隐式传值捕捉函数体内要用到的值,但是x值是单独传引用捕捉,其它的值都是传值捕捉。需要注意的是,当我们隐式传值捕捉之后就不能再这样写了[=,x]这在逻辑上是矛盾的,已经隐式捕捉要用到的值,又去传值捕捉x,两次传值捕捉,逻辑矛盾,报错。

传引用捕捉类似的[&,x]传引用隐式捕捉所有要用到的值,单独传值捕捉x,也是传引用隐式捕捉所以得值后不可以再次传引用捕捉,造成两次捕捉逻辑上的错误。也就是不能这么写[&,&x];\

lambda如果在函数局部域中,它可以捕捉lambda之前定义过的变量,不能捕捉静态局部变量和全局变量,静态局部变量和全局变量也不需要捕捉,lambda可以直接使用,这也就意味着lambda定义在全局域中[]内必须为空。

这里再解释一下,全局域变量和静态变量它们的作用范围是全局,生命周期是程序运行期间,内存地址固定而普通局部变量的生命周期仅仅是所在的作用域作用域也在当前作用域内,比如函数体内定义一个a变量,当函数栈帧销毁时,a变量也会随着一起销毁。我lambda定义在全局域,当要捕获它的时候要么该变量未创建,要么都被销毁了,全局变量又不用去捕获,所以定义在全局域的变量无需去捕获。

捕获的本质是传值捕获会拷贝一份变量供函数体使用,传引用捕获是直接拿到变量的地址来进行使用,传引用捕获的变量要注意生命周期,避免造成悬空引用。这里我们想一下普通的局部变量和全局变量,全局变量的生命周期是程序的整个运行期间,作用域是全局,局部变量的生命周期是在这个函数区间内,出函数就会被销毁,​​局部域可以访问全局变量,但全局域无法直接访问局部变量​​。如果lambda在全局域定义去捕获局部域变量可能导致未定义的行为,比如变量已经销毁,但是还去使用,这种行为未定义。对于全局域的变量大家都可以使用它无需去捕获。

默认情况下,传值捕捉到的变量是被const修饰的,要想取消常性,需要加mutable。

auto func7 = [=]()mutable{};

3,lambda的应用

在学习 lambda 表达式之前,我们的使⽤的可调⽤对象只有函数指针和仿函数对象,函数指针的
类型定义起来⽐较⿇烦,仿函数要定义⼀个类,相对会⽐较⿇烦。使⽤ lambda 去定义可调⽤对
象,既简单⼜⽅便。
lambda 在很多其他地⽅⽤起来也很好⽤。⽐如线程中定义线程的执⾏函数逻辑,智能指针中定
制删除器等, lambda 的应⽤还是很⼴泛的,以后我们会不断接触到。
struct Goods
{
string _name; // 名字
double _price; // 价格
int _evaluate; // 评价
// ...
Goods(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{}
};
struct ComparePriceLess
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price < gr._price;
}
};
struct ComparePriceGreater
{
bool operator()(const Goods& gl, const Goods& gr)
{
return gl._price > gr._price;
}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "⾹蕉", 3, 4 }, { "橙⼦", 2.2, 3
}, { "菠萝", 1.5, 4 } };
// 类似这样的场景,我们实现仿函数对象或者函数指针⽀持商品中
// 不同项的⽐较,相对还是⽐较⿇烦的,那么这⾥lambda就很好⽤了
sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._price < g2._price;
});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._price > g2._price;
});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._evaluate < g2._evaluate;
});
sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
return g1._evaluate > g2._evaluate;
});
return 0;
}

4,lambda原理

lambda 的原理和范围for很像,编译后从汇编指令层的⻆度看,压根就没有 lambda 和范围for
这样的东西。范围for底层是迭代器,⽽lambda底层是仿函数对象,也就说我们写了⼀个
lambda 以后,编译器会⽣成⼀个对应的仿函数的类。
仿函数的类名是编译按⼀定规则⽣成的,保证不同的 lambda ⽣成的类名不同,lambda参数/返
回类型/函数体就是仿函数operator()的参数/返回类型/函数体, lambda 的捕捉列表本质是⽣成
的仿函数类的成员变量,也就是说捕捉列表的变量都是 lambda 类构造函数的实参,当然隐式捕
捉,编译器要看使⽤哪些就传那些对象。
上⾯的原理,我们可以透过汇编层了解⼀下,下⾯第⼆段汇编层代码印证了上⾯的原理。
class Rate
{
public:
Rate(double rate)
: _rate(rate)
{}
double operator()(double money, int year)
{
return money * _rate * year;
}
private:
double _rate;
};
int main()
{
double rate = 0.49;
// lambda
auto r2 = [rate](double money, int year) {
return money * rate * year;
};
// 函数对象
Rate r1(rate);
r1(10000, 2);
r2(10000, 2);
auto func1 = [] {
cout << "hello world" << endl;
};
func1();
return 0;
}
// lambda
auto r2 = [rate](double money, int year) {
return money * rate * year;
};
// 捕捉列表的rate,可以看到作为lambda_1类构造函数的参数传递了,这样要拿去初始化成员变量
// 下⾯operator()中才能使⽤
00D8295C lea eax,[rate]
00D8295F push eax
00D82960 lea ecx,[r2]
00D82963 call `main'::`2'::<lambda_1>::<lambda_1> (0D81F80h)
// 函数对象
Rate r1(rate);
00D82968 sub esp,8
00D8296B movsd xmm0,mmword ptr [rate]
00D82970 movsd mmword ptr [esp],xmm0
00D82975 lea ecx,[r1]
00D82978 call Rate::Rate (0D81438h)
r1(10000, 2);
00D8297D push 2
00D8297F sub esp,8
00D82982 movsd xmm0,mmword ptr [__real@40c3880000000000 (0D89B50h)]
00D8298A movsd mmword ptr [esp],xmm0
00D8298F lea ecx,[r1]
00D82992 call Rate::operator() (0D81212h)
// 汇编层可以看到r2 lambda对象调⽤本质还是调⽤operator(),类型是lambda_1,这个类型名
// 的规则是编译器⾃⼰定制的,保证不同的lambda不冲突
r2(10000, 2);
00D82999 push 2
00D8299B sub esp,8
00D8299E movsd xmm0,mmword ptr [__real@40c3880000000000 (0D89B50h)]
00D829A6 movsd mmword ptr [esp],xmm0
00D829AB lea ecx,[r2]
00D829AE call `main'::`2'::<lambda_1>::operator() (0D824C0h)

http://www.cadmedia.cn/news/4020.html

相关文章:

  • 沈阳市人大网站建设时间网站运营指标
  • 网站关键词密这么稀释鸡西网站seo
  • 广东省会城监狱官方网站建设简短的软文范例
  • h5网站建设图标网络营销推广网站
  • 建工厂网站的公司东莞seo代理
  • 怎样创建自己的网站深圳网站seo服务
  • 深圳网站建设的谷歌aso优化
  • 河池市住房和城乡建设厅网站营销案例最新
  • 公司装修设计案例武汉企业seo推广
  • 为拟建设的网站申请一个域名今日国际新闻摘抄
  • 外包做一个网站一般费用seo网络公司
  • 云南省建设厅勘察设计处网站无锡百度信息流
  • 做直销建立个人网站好吗百度seo培训
  • web网站是什么深圳百度seo整站
  • 建设网站的收费扬州seo推广
  • 新津县建设局网站有没有免费的seo网站
  • 网站如何快速被百度收录什么是网络营销策划
  • 潍坊网站建设价链接优化方法
  • jsp网站服务建设开题报告百度搜索关键词排名优化
  • .net做网站用mvc免费百度广告怎么投放
  • 后台java语言做网站排名优化培训
  • 建设政府网站的目的意义网站收录平台
  • 403网站打开免费百度运营优化师
  • 北京网站建设百度排名今日疫情最新情况
  • 英语教育网站建设线上it培训机构
  • 佛山格尔做网站的公司宣传推广计划怎么写
  • 专业信息网站建设方案推广普通话的文字内容
  • 工程行业招聘网站整站优化系统
  • 做网站建设需要会哪些重庆网站seo多少钱
  • 深圳住房与建设网站制作公司官网多少钱