搭建三合一网站手机网站的内容模块
web/
2025/10/5 10:55:49/
文章来源:
搭建三合一网站,手机网站的内容模块,网络技术工程师是干什么的,网站设计时尚点击蓝字关注我们C11 中增加了许多的新特性。在本文中#xff0c;我们来聊一下 lambda 表达式#xff0c;闭包#xff0c;std::function以及std::bind。lambda 表达式C11 中新增了 lambda 表达式这一语言特性。lambda 表达式可以让我们快速和便捷的创建一个 “函数”。下面是… 点击蓝字关注我们C11 中增加了许多的新特性。在本文中我们来聊一下 lambda 表达式闭包std::function以及std::bind。lambda 表达式C11 中新增了 lambda 表达式这一语言特性。lambda 表达式可以让我们快速和便捷的创建一个 “函数”。下面是lambda表达式的语法[ capture-list ] { body } [ capture-list ] ( params ) { body } [ capture-list ] ( params ) - ret { body } [ capture-list ] ( params ) mutable exception attribute - ret { body }这其中capture-list 是需要捕获的变量列表用逗号分隔。其详细说明见下文。params 是 lambda 表达式需要的参数列表写法和函数参数一样不过这里不支持默认参数。ret 指明了lambda表达式的返回值。通过return语句如果编译器能够推断出返回值的类型。或者表达式没有返回值“- ret” 可以省略。body 函数体。mutable 当捕获列表是以复制见下文的形式捕获时默认这些复制的值是 const 的除非指定了 mutable。exception 提供了异常的说明。attribute 对于attribute的描述可以参见这里http://en.cppreference.com/w/cpp/language/attributes这里不多说明。下面我们通过经典的 Hello World 示例来看一下 lambda 表达式auto lambda1 [] {std::cout Hello, World!\n;};
lambda1();这个 lambda 表达式将打印出字符串 “Hello, World!” 。同时我们将这个表达式赋值给 “lambda1” 这个变量然后像调用函数一样调用这个 lambda 表达式。使用 lambda 表达式可以让我们省却定义函数的麻烦以 inline 的方式写出代码这样的代码通常更简洁。并且由于阅读代码时不用寻找函数定义这样的代码也更易读。下面我们来看另外一个例子。这个例子的需求是分两次打印出一个 vector 集合中所有1. 模 5 0 2. 大于 20 的数字。现假设已有这个集合的定义如下vectorint numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };我们最先想到的方法自然是定义两个函数分别按照上面的要求打印出需要的数字它们的定义如下void printNumber1(vectorint numbers) {for (const int i : numbers) {if (i % 5 0) {coutiendl;}}
}void printNumber1(vectorint numbers) {for (const int i : numbers) {if (i % 5 0) {coutiendl;}}
}然后我们在需要的地方调用它们printNumber1(numbers);
printNumber2(numbers);这里逻辑上并没有问题但是这里我们必须先定义这个函数才能使用。而这样的函数可能实际上我们只会使用一次。当工程大到一定程度我们可能不记得每个函数的实现所以函数命名很重要原谅我这里给函数起了很含糊的名字你在实际上工程中请不要这样做为了知道每个函数的实现我们不得不查看函数的定义这无疑给代码的阅读造成了一定的麻烦。下面我们来看看使用lambda表达式如何改善上面说的问题。使用 lambda 表达式我们可以这样写for_each(numbers.begin(), numbers.end(), [] (int i) {if(i % 5 0) {coutiendl;}
});for_each(numbers.begin(), numbers.end(), [] (int i) {if(i 20) {coutiendl;}
});这里我们不用单独定义函数直接以 inline 的方式解决了问题。并且这段代码一气呵成你很直观的看到了执行的逻辑。下面我们再详细看一下 lambda 表达式中的捕获列表的语法它可能是以下几种情况中的一种[] 不捕获任何变量[] 以引用的方式捕获所有变量[] 以复制的方式捕获所有变量[, foo] 以引用的方式捕获foo变量但是以复制的方式捕获其他变量[bar] 以复制的方式捕获bar变量不再捕获任何其他变量[this] 捕获this指针下面我们再以一个例子说明捕获列表的用法。这里我们的需求是打印出一个 vector的所有数字之和同样的我们先以函数的方式来解决这个问题这个函数的定义可以是这样的void printSum(vectorint numbers) {int sum 0;for (const int i : numbers) {sum i;}coutsumendl;
}然后我们在需要的地方调用这个函数vectorint numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };
printSum (numbers);而假设我们用 lambda 表达式来写这样写就可以了vectorint numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };
int sum 0;
std::for_each(numbers.begin(), numbers.end(), [sum] (const int i) { sum i;});
coutsumendl;这里,我们用[sum] 以引用的形式捕获了 sum 这个变量并且在 lambda 表达式中修改了这个变量。这样写是不是比定义函数的方式简洁了很多对于这种能够捕获其定义时上下文变量的函数我们称之为 “闭包”下文还将提到。std::function上文中对于分两次打印出一个vector集合中所有 1. 模 5 0 2. 大于 20 的数字。 这个需求我们的实现其实还不够好。回头看一下 printNumber1 和 printNumber2 这两个函数这两个函数大部分都是重复的它们都需要遍历集合都需要做 if 判断然后打印出结果。实际上我们在项目中经常遇到这个的问题两多个函数有大部分的代码都是一样的其中只有一两行代码有不一样的地方。 其实我们可以对这个不一样的地方再做一个抽象把它们共通起来。具体到这个例子就是无论是 “模 5 0” 还是 “大于 20” 都是满足“某种条件”。而很自然的会想到我们是否可以通过一个类似这样的函数来做这个判断bool func(int i) 然后实现两个函数通过函数指针的形式来完成判断就好了。但是我们马上又意识到这两个函数会很小并且也是只会用一遍而已定义一个函数又太“浪费”了。很自然的我们就会想 lambda。但是lambda 似乎没法转成函数指针。。。C11中提供了一个通用的描述方法就是 std::function。std::function可以 hold 住任何可以通过“()”来调用的对象包括普通函数成员函数lambdastd::bind见下文后的结果std::function的语法是这样template class Ret, class... Args class functionRet(Args...);例如functionbool (int) filter就表达了我们前面需要的那个函数这个函数接受一个 int 值作为参数同时返回一个 bool 作为判断的结果。但同时我们可以用 lambda 表达式直接传递进去。因此上面的代码可以改写成这样void printNumber(vectorint number, functionbool (int) filter) {for (const int i : number) {if (filter(i)) {coutiendl;}}
}然后在需要的地方这样调用即可printNumber(numbers, [] (int i){ return i % 5 0;});
printNumber(numbers, [] (int i){ return i 20;});这种做法是不是又简洁了不少闭包前面提到了 “闭包” 这个词这里我们来聊一下闭包。下面是维基百度对于闭包的定义在计算机科学中闭包英语Closure又称词法闭包Lexical Closure或函数闭包function closures是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在即使已经离开了创造它的环境也不例外。简单来说闭包可以记忆住创建它时候的那些变量。下面我们再通过一个例子来说明。现在假设我们的需求是获取一个集合中最小和最大值并在稍后的时候可能是另外一个函数中打印它们。这里我们常规的做法通常是通过一个函数获取集合的最大最小值然后保存住最后在需要的时候访问这两个值然后打印它们。这样做就会需要解决如果保存和传递最大最小这两个值。但实际上这里我们可以考虑用闭包来实现这个功能让闭包把最大最小两个值捕获下来然后在需要的地方调用就可以了。请看一下下面这段代码void getMinMax(vectorint number, functionvoid () printer) {int min number.front();int max number.front();for (int i : number) {if (i min) {min i;}if (i max) {max i;}}printer [] () {cout min: min endl;cout max: max endl;};
}这里我们通过 functionvoid () printer如果你看不懂 function请看上文传递出这个闭包。然后在需要的地方这样即可functionvoid() printer;
getMinMax(numbers, printer);
......printer();这里的 printer 其实是我们前面从 getMinMax 函数出传出的闭包这个闭包捕获了 min 和 max。我们直接传递这个闭包给需要的地方使用而不用传递裸的两个数值是不是优雅的不少std::bind下面我们再改进一下需求假设我们要打印出 vector中20x40 范围内的值 strong stylefont-size: inherit; color: inherit; line-height: inherit;该怎么办毕竟bool isBetween(int i, int min, int max)这个函数可没法对应上functionbool (int) filter啊参数数量就不一样嘛。这个时候我们可以用 std::bind。std::bind的语法是这样的template class Fn, class... Args bind (Fn fn, Args... args);
template class Ret, class Fn, class... Args bind (Fn fn, Args... args);std::bind 可以将调用函数时的部分参数先指定好留下一部分在真正调用的时候确定。当然你也可以直接指定全部参数在调用时不再指定。这里isBetween中最小最大值其实我们是确定了的即20 和40。而不确定的其实是真正待判断的数字本身那么我们就可以这么做std::bind(isBetween, placeholders::_1, 20, 40);placeholders::_1 的意思是这里是一个占位符在调用的时候将实际传递的第一个参数放到这里。占位符的数量可以是任意多的像这样std::placeholders::_1, std::placeholders::_2, …, std::placeholders::_N。于是乎对于 打印出vector中20这个需求我们在不修改 printNumber 函数的基础上通过定义一个 isBetween 函数bool isBetween( int i, int min, int max) {return i min i max;
}然后再这样就搞定了functionbool(int) filter std::bind(isBetween, placeholders::_1, 20, 40);
printNumber(numbers, filter);当然你甚至可以直接把这里的两行写成一行。如果你不明白这段代码请再看一下 printNumber 函数的定义void printNumber(vectorint number, functionbool (int) filter) {for (const int i : number) {if (filter(i)) {coutiendl;}}
}这里其实调用了 filter(i) 这个函数对象而这个函数对象只接受一个 int 值作为参数然后返回一个 bool 值。functionbool(int) filter std::bind(isBetween, placeholders::_1, 20, 40);绑定之后只缺一个 int 型参数所以正好对应得上。如果不过瘾我们再来看一个 bind 的例子。我们常常需要在程序中调用一些用户传过来的回调函数。而在回调函数中用户常常会需要记录一些状态于是常常希望通过一个对象的成员函数传过来作为回调函数。但是在 C 中这样做是很麻烦的一个事情。因为回调函数的类型我们很难定义。但是结合 std::function 和 std::bind一切变得容易多了。结合前面的例子现在就假设我们的回调函数是需要打印集合中的最大、最小值。这里假设我们是通过一个类来记录和打印值的这个类的定义是这样的class Printer {
private:int min, max;
public:Printer(int x, int y) {min x;max y;}void print() {cout min: min endl;cout max: max endl;}
};由于回调函数不需要参数因此使用回调函数的代码是这样的void usingCallback(functionvoid () print) {print();
}然后我们可以通过下面的方法来调用 print 函数Printer printer Printer(10, 50);
functionvoid () print bind(Printer::print, printer);
usingCallback(print);成员函数其实是类中的方法绑定到一个对象上然后执行调用。这里的代码很直观的表达了这个关系。lambda表达式是如何实现的lambda 表达式是如何实现的呢其实是编译器为我们了创建了一个类这个类重载了()让我们可以像调用函数一样使用。而对于捕获变量的 lambda 表达式来说编译器在创建类的时候通过成员函数的形式保存了需要捕获的变量。似乎也没有什么神奇的地方。但正是由于编译器帮我们实现了细节使我们的代码变得优雅和简洁了许多。*声明本文于网络整理版权归原作者所有如来源信息有误或侵犯权益请联系我们删除或授权事宜。戳“阅读原文”我们一起进步
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/87323.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!