网站建设程序有哪些温州专业手机网站制作多少钱

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

网站建设程序有哪些,温州专业手机网站制作多少钱,太原百度快速优化排名,平面设计与网页设计C11#xff1a;lambda表达式 包装器 lambda表达式包装器functionbind lambda表达式 在C98中#xff0c;如果想对一个结构体数组使用sort排序#xff0c;那么我们就需要自己些仿函数。 比如以下结构体#xff1a; struct Goods {string _name; // 名字double _pric… C11lambda表达式 包装器 lambda表达式包装器functionbind lambda表达式 在C98中如果想对一个结构体数组使用sort排序那么我们就需要自己些仿函数。 比如以下结构体 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;} };随着C语法的发展人们开始觉得上面的写法太复杂了每次为了实现一个algorithm算法都要重新去写一个类如果每次比较的逻辑不一样还要去实现多个类特别是相同类的命名这些都给编程者带来了极大的不便。因此在C11语法中出现了Lambda表达式 lambda语法如下 capture_list mutable - return_type {statement}这个语法看起来比较复杂我先简单讲拆分一下各个部分 [capture_list]捕捉列表(parameters)参数列表mutable一个关键字- return_type返回值类型{statement} 函数体 比如这是一个完整的lambda表达式 auto add mutable - int { return a b; };很明显的看出以上函数就是传入两个整数然后返回两数之和。 lambda表达式有很多种省略情况 muteble可以省略改关键字的具体功能后续讲解 auto add - int { return a b; };函数的返回值- return_type可以省略lambda表达式可以自己推导返回类型 auto add { return a b; };当函数没有参数时(parameters)参数列表可以省略 auto say_hello [] { cout hello world! endl; };以上函数就已经是一个非常简单的lambda表达式了。那么lambda表达式有什么用呢 lambda会返回一个仿函数对象 比如auto add { return a b; };其实add就是一个仿函数对象了我们可以直接按照调用函数的方式来调用这个仿函数add(1, 2);。但是要注意 lambda表达式返回的仿函数对象其类名是随机的因此必须使用auto来接受这个仿函数对象。 现在我们再讲讲lambda表达式最前面的[]的作用其名称为捕获列表可以捕获父作用域中所有变量。 比如这样 int x 1; int y 2;auto add [x, y] {return x y; };以上代码中[x, y]就是在捕获父作用域中的两个变量那么函数体中就可以直接使用这两个变量了。如果直接通过变量名捕获此时是传值调用修改函数体内部的变量不会影响父作用域的变量。 但是通过直接传值捕获的变量自带const属性不允许修改比如以下代码 int x 1; int y 2;auto add [x, y] {x 5;y 5;};此时代码就会报错因为x和y是通过捕获列表捕获的变量传入的参数带有const属性不允许修改。此时就要用到mutable了mutable可以让被捕获的参数可以修改。 auto add [x, y] mutable{x 5;y 5;};但是这个写法还是错误的如果使用了mutable就算没有通过参数列表传参()也不可以省略 auto add x, y mutable{x 5;y 5;};我们也可以以传引用的方式来捕获变量只需要在变量名前加上操作符 int x 1; int y 2;auto add [x, y]{x 5;y 5;};此时修改函数内部的x和y就是在修改父作用域的x和y了。这里要注意如果使用了传引用捕获变量就算没有mutable也可以修改参数。 另外的lambda还提供了一次性捕获所有父作用域变量的语法只需要在捕获列表中写即可 int x 1; int y 2;auto add []{return x y;};[]就是一次性捕获了所有父作用域变量的过程我们可以直接在函数体内部使用父作用域的所有变量。 不过[]是以传值的形式捕获父作用域所有变量而[]是以传引用的形式捕获父作用域所有变量 int x 1; int y 2;auto add []{x 5;y 5;};另外的我们还可以把传值和传引用混合使用让部分参数传参部分参数传引用。 [x, y]以传值的形式捕获x以传引用的形式捕获y [, x]以传值的形式捕获父作用域所有变量以传引用的形式捕获x [, x]以传值的形式捕获x以传引用的形式捕获父作用域所有变量 接下来我再次汇总一下lambda的语法 各个部分 [capture_list]捕捉列表可以捕获父作用域的任意变量有传参和传引用两种形式(parameters)参数列表如果没有参数可以省略mutable如果以传参形式捕获参数不可修改参数加上该关键字后可以修改- return_type返回值类型可以省略lambda会自动推导{statement} 函数体不可省略 有了lambda表达式后我们在需要仿函数的地方就无需额外写一个仿函数的类而是直接写一个lambda表达式比如最开始的按照价格排序 vectorGoods v { { 苹果, 2.1, 5 }, { 香蕉, 3, 4 }, { 橙子, 2.2, 3 }, { 菠萝, 1.5, 4 } };sort(v.begin(), v.end(), {return g1._price g2._price; });sort(v.begin(), v.end(), {return g1._price g2._price; });因为省略了返回值我们以比函数还简短的方式完成了仿函数的书写。 但是有一个情况那就是模板参数中的lambda表达式。 如果我们想要给一个优先级队列priority_queue传入一个less仿函数 priority_queueint, vectorint, lessint q;其中lessint就是我们的仿函数但是lessint不是仿函数实例化出的对象而是一个仿函数类型。也就是说模板参数中需要的不是仿函数对象而是仿函数类型。但是lambda表达式整体返回的类型是仿函数对象因此以下写法是错误的 priority_queueint, vectorint, {return i1 - i2; } q;我们不能直接把lambda当作模板参数传入此时就要使用decltype来推导原先的类型 auto intLess {return i1 - i2; }; priority_queueint, vectorint, decltype(intLess) q;包装器 在寄快递的时候快递会进行一次包装这样我们就可以统一的在上面贴上快递信息随后以统一的形式管理所有快递。包装器也是如此包装器可以将具有相似属性的东西包装起来成为一个整体。 function 如果一个变量f可以按照f()的形式调用函数那么称f是一个可调用对象 回顾一下现在我们有那些可调用对象 函数指针函数名函数名的本质就是函数指针仿函数实例化出的对象lambda表达式 这三者都可以直接加一对()进行函数调用。它们都有各自的缺点 函数指针函数名类型复杂不好用仿函数实例化出的对象哪怕参数返回值都相同仿函数之间的类型也不同lambda表达式类型是随机的必须用auto接收 可以看到这三者都有类型方面的大问题我们也没有一种方式可以把所有参数类型和返回值类型相同的函数统一的管理起来让它们都变成一个类型 包装器function就可以做到该工作function被包含在头文件functional中是一个类模板模板原型如下 template class T function;template class Ret, class… Args class functionRet(Args…);其语法为function返回值(参数列表)只要所有返回值和参数列表相同的可调用对象经过这一层封装都会变成相同的类型。 比如我们现在有如下三个函数 double func(double x) {return x / 2; }struct Functor {double operator()(double x){return x / 3;} };int main() {auto lambadaFunc {return d / 4; };return 0; }分别是func函数Functor仿函数以及lambda表达式lambadaFunc 。它们的返回值都是double参数类型也是double因此可以经过包装器包装为functiondoubledouble。 如下 functiondouble(double) func1 func; functiondouble(double) func2 Functor(); functiondouble(double) func3 lambadaFunc;此时三者的类型就都是functiondouble(double) 了。 有了这一层包装器在需要统一管理函数时就很方便了。比如说我现在要搞一个计算器的map往map中输入哪一个操作符就调用哪一个函数 mapchar, functionint(int, int) opFuncMap {{, {return x y; }},{-, {return x - y; }},{*, {return x * y; }},{/, {return x / y; }} };由于 - * /的函数都是lambda表达式四个表达式的类型都是不可知的map的第二个模板参数就不知道是啥了。不过我们可以通过function进行包装把所有函数都包装成functionint(int, int)类型最后就可以通过map统一管理了。 我们最后就可以这样调用函数 opFuncMap; opFuncMap-; opFuncMap*; opFuncMap/;bind bind翻译后为绑定其可以对参数进行绑定。其主要有两个功能改变参数顺序给指定参数绑定固定值。 语法 bind是一个函数模板其接收多个参数第一个参数为可调用对象后续参数为该可调用对象的参数。这个参数的语法比较特别C11后新增一个命名空间域placeholders其内部会存储很多变量这些变量用于函数的传参变量的名字为_x表示第x个参数。 比如以下代码中 int sub(int a, int b) {return a - b; }int main() {auto f1 bind(sub, placeholders::_2, placeholders::_1);f1(3, 5);return 0; }对于bind(sub, placeholders::_2, placeholders::_1);来说sub这个参数是一个可调用对象。 placeholders::_2表示第二个参数placeholders::_1表示第一个参数。 比如这个f1最后拿到了这个bind封装的函数那么f1(3, 5)执行的并不是3 - 5而是5 - 3。 这是因为我们特地把placeholders::_2写在前面f1(3, 5)把第二个5传给了placeholders::_2把第一个3传给了placeholders::_1。 而最后调用sub函数的时候placeholders::_1会被传给sub的第一个参数placeholders::_2则会传给sub的第而个参数。这样我们就完成了函数参数顺序的改变。 再比如以下代码 int sub(int a, int b) {return a - b; }int main() {auto f2 bind(sub, 3.14, placeholders::_1);f2(10);return 0; }bind(sub, 3.14, placeholders::_1)第一个参数为可调用对象sub第二个参数是一个固定值3.14那么如果通过f2调用该sub函数参数a都固定为3.14。比如f2(10)就只传了一个参数再去调用sub时就完成3.14 - 10的操作。因此我们可以通过sub把某个参数绑定为固定值。