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

Stella981
• 阅读 673

实例代码

// lambda表达式,for_each、find_if简介


#include <iostream>
#include <vector>
#include <map>
#include <functional>
#include <algorithm>

using namespace std;


class CT{
public:
    int m_i = 5;
    void myfuncpt(int x, int y){
        auto mylambdal = [=] { // 无论是this, 还是& 或者 =  都可以达到访问类成员的目的
            return m_i; //因为有了this ,这个访问合法的, 用& = 也行
        };

        auto mylambda2 = [this, &x, y] { // 无论是this, 还是& 或者 =  都可以达到访问类成员的目的
            x = 5;
            //y = 6; // 错误
            return m_i; //因为有了this ,这个访问合法的, 用& = 也行
        };
        auto mylambda3 = [&, x] { // 无论是this, 还是& 或者 =  都可以达到访问类成员的目的
            // x = 5; //错误
            return m_i; //因为有了this ,这个访问合法的, 用& = 也行
        };

        cout << mylambdal() << endl;
    }
};


void myfunc(int i){
    cout << i << endl;
}


int main(int argc, const char * argv[]) {

    // 一: 用法简介
    // C++ 11 中引入, lambda 表达式, 它定义了 一个匿名函数, 并且可以捕获一定范围内的变量。
    auto f = [](int a)->int{
        return a + 1;
    };
    cout << f(1) << endl;

    //特点:
    // a). 是个匿名函数,也可以理解为“可调用的代码单元”,或者理解成 未命名的内联函数。
    // b). 它也有一个返回类型,一个参数列表,一个函数体
    // c). 与函数不同的是,lambda表达式可以在函数内部定义。这个是常规函数左不到的;

    // 格式: [捕捉列表](参数列表)->返回类型 { 函数体 };
    // a). 这是一个返回类型后置这种语法(lambda表达式的返回类型后置是必须的,这个语法就是这么规定的)
    // 因为很多时候lambda表达式返回值特别明显,所以允许lambda表达式返回类型,编译器可以自动推导。
    // lambda 参数可以有默认值
    auto f1 = [](int a = 8)->int{
        return a + 1;
    };
    cout << f1() << endl;

    //大家要注意:编译器并不是总能推断出返回值类型,如果编译器推导不出来的时候,它会报错,这时就需要你自己显示给定类型。
    // b). 没有参数的时候,参数列表可以省略,甚至()也能省略,如下:
    auto f2 = [](){ return 1; };
    auto f3 = [] { return 2; };

    cout << f2() << endl;
    cout << f3() << endl;
    // c). 捕获列表[] 和函数体不能省。
    // d). lambda调用方法和普通函数相同,都是使用()这种函数调用运算符;
    // e). lambda表达式可以不返回任何类型, 不返回任何类型就是void;
    auto f4 = []{};
    // f). 函数体末尾的分号不能省略;


    // 二: 捕获列表 : [...] 中括号中的内容: 通过捕获列表来捕获一定范围内的变量。范围指的意思是?
    // a). [] 不捕获任何变量, 但不包括静态局部变量 。 lambda 可以直接使用局部静态变量
    int i = 9;
    static int j = 10;
    auto f5 = [] {
        //    return  i; // 报错 (无法捕获外部变量),不认识这个i 在哪里定义
        return  j; // 正确  可以捕获局部静态变量
    };

    // b). [&] 捕获外部作用域中所有变量,并作为引用在函数体内使用
    auto f6 = [&] {
        i = 5; //因为&的存在,那么就允许给i赋值,从而也就改变了i的值
        return i;
    };

    cout << f6() << endl;
    cout << i << endl;

    // c). [=] :捕获外部作用域中所有变量,并作为副本(按值)在函数中使用,也就是可以用它的值,但不许给它赋值。
    auto f7 = [=] {
        //i = 5; // 报错  不可以给它赋值,因为是以值得方式捕获。
        return i;
    };
    cout << f7() << endl;

    // d). [this] : 一般用于类中,捕获当前类中this指针,让lambda表达式有和当前类成员函数同样的访问权限。
    //如果[] 中已经使用了 & 或者 = ,那么默认就已经使用了this ,说白了,捕获this的目的就是为了在lambda中使用当前类的成员函数和成员变量;
    CT ct;
    ct.myfuncpt(3, 4);

    // e). [变量名] : 如果是多个变量名, 则彼此之间用“,”分隔。 [变量名] 表示按值捕获变量名代表的变量,同时不捕获其他变量。
    // 捕获时需要消耗资源,所以用到什么就捕获什么,不需要全部去捕获;
    // [&变量名] : 按引用捕获变量名代表的变量,同时不捕获其他变量;

    // f). [=, &变量名]
    // 按值捕获所有外部变量,但按引用捕获&中所指的变量,这里这个“ = ” 必须写在开头位置。开头这个位置表示默认捕获方式。
    // 也就是说,这个捕获列表,第一个位置表示的是默认捕获方式(隐式捕获方式),后续其他的都是显式捕获方式。

    // g). [&, 变量名]: 按引用来捕获所有外部变量,但按值捕获变量名所代表的变量,这里“ & ”必须写在开头位置,开头

    // 总结: lambda表达式对于能访问的外部变量控制得非常细致。


    // 三: lambda表达式延迟调用易出错细节分析
    int x = 5;
    auto ff = [=]{ //解释 当遇到auto这一行 也就是在捕获的这个时刻, x的值就已经被赋值到了这个f中了。
        return x;
    };
    x = 10;
    cout << ff() << endl; // 我们认为是10 但实际是 5  ,原因在上面解释
    //也就是说,凡是按值捕获的外部变量,在lambda表达式定义的这个时刻,所有这些外部变量就被复制了一份存储在lambda表达式变量中。


    // 四: lambda表达式中的mutable(易变的)
    auto fff = [=]() mutable {  // 注意: 加 mutable ,则“() ” 圆括号不能省略。
        x = 6; //正常情况下, 这里是不可以修改的, 因为加了 mutable 这里变成了可以修改
        return x;
    };

    // 五: lambda表达式的类型及存储
    // C++ 11中, lambda表达式的类型被称呼为 “闭包类型(Closure Type)”;
    // 闭包: 函数内的函数(可调用对象)。本质上就是lambda表达式创建的运行时期的对象。
    // lambda 表达式是一种比较特殊的,匿名的,类类型【闭包类】的对象,(也就是定义了一个类类型,又生成一个匿名的该类类型的对象【闭包】)
    // 我们可以认为它是一个带有operator()的类类型对象。也就是仿函数(函数对象);
    // 所以,我们也可以用std::function 和 std::bind来保存和调用lambda表达式。每个lambda都会触发编译器给咱们生成一个独一无二的类类型。

    // lambda 表达式这种语法,是我们可以就地定义匿名函数(就地封装短小的功能闭包);
    auto f11 = []{}; // f11是个未命名的类类型对象。

    // std::function 和 lambda结合
    std::function<int(int)> fc1 = [](int tv) {return tv; };
    cout << fc1(15) << endl;  // 正常运行 输出 15

    std::function<int(int)> fc2 = std::bind( // bind 第一个参数是函数指针,  第二个参数开始就是真正的函数参数
        [](int tv) {
        return tv;
    },
        16
        );
    cout << fc2(15) << endl;  // 正常运行 输出 16 因为在上面std::bind中绑定了16

    //不捕获任何变量的lambda表达式,也就是捕获列表为空,可以转换成一个普通的函数指针;
    using functype = int(*)(int); // 定义一个函数指针类型;
    functype fp = [](int tv) {return tv; };
    cout << fp(17) << endl;   // 正常运行 输出 17

    // (5.1) 语法糖概念
    //语法糖: 一种便捷的写法  的意思
    //语法糖是指基于语言现有特性构建出一个东西,程序员用起来会很方便,但它没有增加语言的原有功能;
    //所以,这个lambda表达式,大家也可以看成是定义仿函数闭包(函数中的函数)的语法糖。


    // 六: lambda表达式再演示和优点总结
    // (6.1) for_each简介: 是个函数模板
    vector<int> myvector = { 10, 20, 30, 40, 50 };
    for_each(myvector.begin(), myvector.end(), myfunc);

    int isum = 0;
    for_each(myvector.begin(), myvector.end(), [&isum](int val){
        isum += val;
        cout << val << endl;
    });
    cout << "sum = " << isum << endl; // 150

    // (6.2) find_if简介 : 函数模板
    //用来查找一个东西,查什么东西呢,取决于它的第三个参数,它的第三个参数也是个函数对象(lambda 表达式)
    auto result = find_if(myvector.begin(), myvector.end(), [](int val){
        cout << val << endl;
        if (val > 15){
            return true; // 返回true表示停止遍历
        }
        return false; //只要返回false ,那么find_if就不停的遍历myvector。一直到返回true或者遍历完为止。
    });
    // 如果find_if第三个参数这个可调用对象(lambda)返回true, find_if 就停止遍历
    // find_if 的调用返回一个迭代器,指向第一个满足条件的元素,如果这样的元素不存在,则这个迭代器会指向myvector.end()
    if (result == myvector.end()){
        cout << "没找到" << endl;
    }
    else{
        cout << "找到了,结果为:" << *result << endl;
    }


    return 0;
}
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java 8 lambda表达式中的异常处理
java8lambda表达式中的异常处理简介java8中引入了lambda表达式,lambda表达式可以让我们的代码更加简介,业务逻辑更加清晰,但是在lambda表达式中使用的FunctionalInterface并没有很好的处理异常,因为JDK提供的这些FunctionalInterface通常都是没有抛出异常的,这意味着需要我们自
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java8实战读书笔记:复合Lambda表达式
本节将重点探讨复合Lambda表达式的使用。在阅读本篇之前建议您先阅读:java8实战读书笔记:Lambda表达式语法与函数式编程接口,因为本篇是上篇的补充。(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIzNzgy
Wesley13 Wesley13
3年前
Java8特性
Java8又称jdk1.8。主要新特性:Lambda表达式 −Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。方法引用 −方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
Wesley13 Wesley13
3年前
Java8
  不管lambda表达式还是Stream流式编程,Function、Consumer、Supplier、Predicate四个接口是一切函数式编程的基础。下面我们详细学习这四个巨头,interfaceSupplier<T    该接口的中文直译是“提供者”,可以理解为定义一个lambda表达式,
Wesley13 Wesley13
3年前
Java8 新增特性 Lambda表达式
               聊聊Lambda  背景:    早在2014年oracle发布了jdk8,在里面增加了lambda模块。于是java程序员们又多了一种新的编程方式:函数式编程,也就是lambda表达式。    以下整理关于Lambda表达式资料(转载地址:https:/
Wesley13 Wesley13
3年前
Java 8 Lambda表达式的使用
lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。Lambda表达式还增强了集合库。JavaSE8添加了2个对集合数据进行批量操作的包:java.util.function包以及java.util.stre
Stella981 Stella981
3年前
C++语法学习笔记四十八:单例设计模式共享数据分析、解决,call_once
实例代码://include<iostreaminclude<vectorinclude<threadinclude<mutexusingnamespacestd;classA{public:
Wesley13 Wesley13
3年前
Java8—一万字的Lambda表达式的详细介绍与应用案例
  基于Java8详细介绍了lambda表达式的语法与使用,以及方法引用、函数式接口、lambda复合等Java8的新特性!文章目录1Lambda的概述2函数式接口2.1Consumer消费型接口2.2Supplier供给型接口2.3Function<T,R函数型接口
Stella981 Stella981
3年前
C++ Map运用实例
CMap运用实例!复制代码(https://common.cnblogs.com/images/copycode.gif)include<mapinclude<string\include<iostreaminclude<iomanipusingnamespacestd;intmain(){