C++语法学习笔记三十一: lambda表达式,for_each、find_if简介

实例代码

1// lambda表达式,for_each、find_if简介 2 3 4#include <iostream> 5#include <vector> 6#include <map> 7#include <functional> 8#include <algorithm> 9 10using namespace std; 11 12 13class CT{ 14public: 15 int m_i = 5; 16 void myfuncpt(int x, int y){ 17 auto mylambdal = [=] { // 无论是this, 还是& 或者 = 都可以达到访问类成员的目的 18 return m_i; //因为有了this ,这个访问合法的, 用& = 也行 19 }; 20 21 auto mylambda2 = [this, &x, y] { // 无论是this, 还是& 或者 = 都可以达到访问类成员的目的 22 x = 5; 23 //y = 6; // 错误 24 return m_i; //因为有了this ,这个访问合法的, 用& = 也行 25 }; 26 auto mylambda3 = [&, x] { // 无论是this, 还是& 或者 = 都可以达到访问类成员的目的 27 // x = 5; //错误 28 return m_i; //因为有了this ,这个访问合法的, 用& = 也行 29 }; 30 31 cout << mylambdal() << endl; 32 } 33}; 34 35 36void myfunc(int i){ 37 cout << i << endl; 38} 39 40 41int main(int argc, const char * argv[]) { 42 43 // 一: 用法简介 44 // C++ 11 中引入, lambda 表达式, 它定义了 一个匿名函数, 并且可以捕获一定范围内的变量。 45 auto f = [](int a)->int{ 46 return a + 1; 47 }; 48 cout << f(1) << endl; 49 50 //特点: 51 // a). 是个匿名函数,也可以理解为“可调用的代码单元”,或者理解成 未命名的内联函数。 52 // b). 它也有一个返回类型,一个参数列表,一个函数体 53 // c). 与函数不同的是,lambda表达式可以在函数内部定义。这个是常规函数左不到的; 54 55 // 格式: [捕捉列表](参数列表)->返回类型 { 函数体 }; 56 // a). 这是一个返回类型后置这种语法(lambda表达式的返回类型后置是必须的,这个语法就是这么规定的) 57 // 因为很多时候lambda表达式返回值特别明显,所以允许lambda表达式返回类型,编译器可以自动推导。 58 // lambda 参数可以有默认值 59 auto f1 = [](int a = 8)->int{ 60 return a + 1; 61 }; 62 cout << f1() << endl; 63 64 //大家要注意:编译器并不是总能推断出返回值类型,如果编译器推导不出来的时候,它会报错,这时就需要你自己显示给定类型。 65 // b). 没有参数的时候,参数列表可以省略,甚至()也能省略,如下: 66 auto f2 = [](){ return 1; }; 67 auto f3 = [] { return 2; }; 68 69 cout << f2() << endl; 70 cout << f3() << endl; 71 // c). 捕获列表[] 和函数体不能省。 72 // d). lambda调用方法和普通函数相同,都是使用()这种函数调用运算符; 73 // e). lambda表达式可以不返回任何类型, 不返回任何类型就是void; 74 auto f4 = []{}; 75 // f). 函数体末尾的分号不能省略; 76 77 78 // 二: 捕获列表 : [...] 中括号中的内容: 通过捕获列表来捕获一定范围内的变量。范围指的意思是? 79 // a). [] 不捕获任何变量, 但不包括静态局部变量 。 lambda 可以直接使用局部静态变量 80 int i = 9; 81 static int j = 10; 82 auto f5 = [] { 83 // return i; // 报错 (无法捕获外部变量),不认识这个i 在哪里定义 84 return j; // 正确 可以捕获局部静态变量 85 }; 86 87 // b). [&] 捕获外部作用域中所有变量,并作为引用在函数体内使用 88 auto f6 = [&] { 89 i = 5; //因为&的存在,那么就允许给i赋值,从而也就改变了i的值 90 return i; 91 }; 92 93 cout << f6() << endl; 94 cout << i << endl; 95 96 // c). [=] :捕获外部作用域中所有变量,并作为副本(按值)在函数中使用,也就是可以用它的值,但不许给它赋值。 97 auto f7 = [=] { 98 //i = 5; // 报错 不可以给它赋值,因为是以值得方式捕获。 99 return i; 100 }; 101 cout << f7() << endl; 102 103 // d). [this] : 一般用于类中,捕获当前类中this指针,让lambda表达式有和当前类成员函数同样的访问权限。 104 //如果[] 中已经使用了 & 或者 = ,那么默认就已经使用了this ,说白了,捕获this的目的就是为了在lambda中使用当前类的成员函数和成员变量; 105 CT ct; 106 ct.myfuncpt(3, 4); 107 108 // e). [变量名] : 如果是多个变量名, 则彼此之间用“,”分隔。 [变量名] 表示按值捕获变量名代表的变量,同时不捕获其他变量。 109 // 捕获时需要消耗资源,所以用到什么就捕获什么,不需要全部去捕获; 110 // [&变量名] : 按引用捕获变量名代表的变量,同时不捕获其他变量; 111 112 // f). [=, &变量名] 113 // 按值捕获所有外部变量,但按引用捕获&中所指的变量,这里这个“ = ” 必须写在开头位置。开头这个位置表示默认捕获方式。 114 // 也就是说,这个捕获列表,第一个位置表示的是默认捕获方式(隐式捕获方式),后续其他的都是显式捕获方式。 115 116 // g). [&, 变量名]: 按引用来捕获所有外部变量,但按值捕获变量名所代表的变量,这里“ & ”必须写在开头位置,开头 117 118 // 总结: lambda表达式对于能访问的外部变量控制得非常细致。 119 120 121 // 三: lambda表达式延迟调用易出错细节分析 122 int x = 5; 123 auto ff = [=]{ //解释 当遇到auto这一行 也就是在捕获的这个时刻, x的值就已经被赋值到了这个f中了。 124 return x; 125 }; 126 x = 10; 127 cout << ff() << endl; // 我们认为是10 但实际是 5 ,原因在上面解释 128 //也就是说,凡是按值捕获的外部变量,在lambda表达式定义的这个时刻,所有这些外部变量就被复制了一份存储在lambda表达式变量中。 129 130 131 // 四: lambda表达式中的mutable(易变的) 132 auto fff = [=]() mutable { // 注意: 加 mutable ,则“() ” 圆括号不能省略。 133 x = 6; //正常情况下, 这里是不可以修改的, 因为加了 mutable 这里变成了可以修改 134 return x; 135 }; 136 137 // 五: lambda表达式的类型及存储 138 // C++ 11中, lambda表达式的类型被称呼为 “闭包类型(Closure Type)”; 139 // 闭包: 函数内的函数(可调用对象)。本质上就是lambda表达式创建的运行时期的对象。 140 // lambda 表达式是一种比较特殊的,匿名的,类类型【闭包类】的对象,(也就是定义了一个类类型,又生成一个匿名的该类类型的对象【闭包】) 141 // 我们可以认为它是一个带有operator()的类类型对象。也就是仿函数(函数对象); 142 // 所以,我们也可以用std::function 和 std::bind来保存和调用lambda表达式。每个lambda都会触发编译器给咱们生成一个独一无二的类类型。 143 144 // lambda 表达式这种语法,是我们可以就地定义匿名函数(就地封装短小的功能闭包); 145 auto f11 = []{}; // f11是个未命名的类类型对象。 146 147 // std::function 和 lambda结合 148 std::function<int(int)> fc1 = [](int tv) {return tv; }; 149 cout << fc1(15) << endl; // 正常运行 输出 15 150 151 std::function<int(int)> fc2 = std::bind( // bind 第一个参数是函数指针, 第二个参数开始就是真正的函数参数 152 [](int tv) { 153 return tv; 154 }, 155 16 156 ); 157 cout << fc2(15) << endl; // 正常运行 输出 16 因为在上面std::bind中绑定了16 158 159 //不捕获任何变量的lambda表达式,也就是捕获列表为空,可以转换成一个普通的函数指针; 160 using functype = int(*)(int); // 定义一个函数指针类型; 161 functype fp = [](int tv) {return tv; }; 162 cout << fp(17) << endl; // 正常运行 输出 17 163 164 // (5.1) 语法糖概念 165 //语法糖: 一种便捷的写法 的意思 166 //语法糖是指基于语言现有特性构建出一个东西,程序员用起来会很方便,但它没有增加语言的原有功能; 167 //所以,这个lambda表达式,大家也可以看成是定义仿函数闭包(函数中的函数)的语法糖。 168 169 170 // 六: lambda表达式再演示和优点总结 171 // (6.1) for_each简介: 是个函数模板 172 vector<int> myvector = { 10, 20, 30, 40, 50 }; 173 for_each(myvector.begin(), myvector.end(), myfunc); 174 175 int isum = 0; 176 for_each(myvector.begin(), myvector.end(), [&isum](int val){ 177 isum += val; 178 cout << val << endl; 179 }); 180 cout << "sum = " << isum << endl; // 150 181 182 // (6.2) find_if简介 : 函数模板 183 //用来查找一个东西,查什么东西呢,取决于它的第三个参数,它的第三个参数也是个函数对象(lambda 表达式) 184 auto result = find_if(myvector.begin(), myvector.end(), [](int val){ 185 cout << val << endl; 186 if (val > 15){ 187 return true; // 返回true表示停止遍历 188 } 189 return false; //只要返回false ,那么find_if就不停的遍历myvector。一直到返回true或者遍历完为止。 190 }); 191 // 如果find_if第三个参数这个可调用对象(lambda)返回true, find_if 就停止遍历 192 // find_if 的调用返回一个迭代器,指向第一个满足条件的元素,如果这样的元素不存在,则这个迭代器会指向myvector.end() 193 if (result == myvector.end()){ 194 cout << "没找到" << endl; 195 } 196 else{ 197 cout << "找到了,结果为:" << *result << endl; 198 } 199 200 201 return 0; 202}
点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java 8 lambda表达式中的异常处理

java8lambda表达式中的异常处理简介java8中引入了lambda表达式,lambda表达式可以让我们的代码更加简介,业务逻辑更加清晰,但是在lambda表达式中使用的FunctionalInterface并没有很好的处理异常,因为JDK提供的这些FunctionalInterface通常都是没有抛出异常的,这意味着需要我们自

java8实战读书笔记:复合Lambda表达式

本节将重点探讨复合Lambda表达式的使用。在阅读本篇之前建议您先阅读:java8实战读书笔记:Lambda表达式语法与函数式编程接口,因为本篇是上篇的补充。(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIzNzgy

Java8特性

Java8又称jdk1.8。主要新特性:Lambda表达式 −Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。方法引用 −方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

Java8

  不管lambda表达式还是Stream流式编程,Function、Consumer、Supplier、Predicate四个接口是一切函数式编程的基础。下面我们详细学习这四个巨头,interfaceSupplier<T    该接口的中文直译是“提供者”,可以理解为定义一个lambda表达式,

Java8 新增特性 Lambda表达式

               聊聊Lambda  背景:    早在2014年oracle发布了jdk8,在里面增加了lambda模块。于是java程序员们又多了一种新的编程方式:函数式编程,也就是lambda表达式。    以下整理关于Lambda表达式资料(转载地址:https:/