更多教程笔记请查看我的上一篇文章:点击跳转

C++11(lambad表达式)自学之旅开始!

本次将介绍C++11里新的函数写法:labmda表达式

lambda表达式很早就有了,后来被C++11引入,原本我对lambda的语法一点兴趣都没有,可是之前还在公司的一个学长跟我说lambda表达式很有用,所以我就去学了。

这东西确实挺厉害的,据说发明他的作者认为一切程序的本质都是函数,后面还用了数学公式证明,不得不说数学厉害的人写程序真的强,随便就弄出一个定理和语法规范,让我们都得跟着学。

lambda表达式语法

// lambda表达式
#include <iostream>
using namespace std;

//正常函数的写法 有名字,有返回值,有参数列表
int fun(int a, int b)
{
    return a + b;
}
int main()
{
    /* lambda表达式的本质就是一个仿函数(匿名函数)
    也就是一个类里面有一个operator()() 重载小括号
    它没有名字
    */

    //这是lambda表达式的写法,匿名的,前面的[]符号代表正常函数中的fun也就是名字,后面的()符号
    //代表函数的参数列表,再后面的->int代表函数的返回值类型,也就是正常函数前面的int,void之类的
    //紧接着后面的{}里写函数的主体,也就是函数的具体实现,最后一个()里写调用函数时的传参

    // 1.[]函数名字 2.()参数列表 3.->int函数返回值类型 4.{}函数主体
    //最后返回的值在最前面用一个变量来接收
    int c = [](int a, int b) -> int
    {
        //函数主体
        return a + b;
    }(1, 2); // 5.()调用函数传参 在定义时就可以立即匿名调用 但是在其他地方就用不了了

    //上面等价于下面
    int c2 = fun(1, 2);
    cout << c << endl;

    //第二种写法 使用自动类型推导 auto将会把它推成一个匿名函数的类型
    //也就是把f当作这个匿名函数的名字,那么在其他地方就可以通过f调用到这个匿名函数
    auto f = [](int a, int b) -> int
    {
        //函数主体
        return a + b;
    };

    // f被当作了上面这个匿名函数的名字,通过f调用到了这个匿名函数
    int c3 = f(1, 2);
    cout << c3 << endl;

    return 0;
}

lambda表达式实战-嵌套

// lambda表达式实战-嵌套
#include <iostream>
using namespace std;
int main()
{

    // lambda嵌套lambda
    // lambda 跟普通类型一样可以作为参数也可以作为返回值 这里就是作为返回值 也叫Currying
    //函数编程 用于多线程 并发
    //外面的函数体返回的是内部的函数体的内容
    int c = [](int n)
    {
        //内部的函数体可以使用外部的函数体的形参,只要在内部的函数体的[]里加上外部的变量名即可
        return [n](int x)
        {
            //只有一条return语句可以不写返回类型,编译器会推断 其他情况不写就是void
            return n + x;
        }(1); // 匿名调用内部的函数 1传给x
    }(2);     // 匿名调用外部的函数 2传给n

    cout << c << endl;

    //通过自动类型推导为匿名函数加上名字,可以让它在其他地方调用
    auto f = [](int n)
    {
        cout << "n= " << n << endl;
        return [n](int x)
        {
            cout << "x= " << x << endl;
            return n + x;
        };
    };

    //调用函数传参 1传给n 2传给x 先传参调用外层,再传参调用里层
    int c2 = f(1)(2);

    cout << c2 << endl;
    return 0;
}

lambda按值捕获-mutable

// mutable-lambda按值捕获
#include <iostream>
using namespace std;
int main()
{
    // lambda第三种写法
    int t = 10;
    //如果想要获取外部(上面)的t的值,那么就不能写在参数列表()里,参数列表是在调用时传的
    //要写在[]里,[]是捕获列表,也就是匿名函数的捕获区,在捕获区里写变量名即可捕获外部的变量
    //在[]里捕获的变量默认是const类型,无法被修改,只有在()后面加了mutable关键字后[]捕获的值才能被修改
    auto f = [t]() mutable
    {
        return ++t;
    };

    //在用mutable进行捕获时会单独拷贝出一份地址,这样在里面修改的值会一直保存在函数体里,下一次调用时会依旧使用上一次拷贝的值
    //这个值只在当前的匿名函数内部是有效的,就算外部有一个一模一样的匿名函数调用也只拷贝原来变量的值,无法拷贝之前匿名函数的值
    // 11
    cout << f() << endl;
    // 12
    cout << f() << endl;

    //按值捕获,在函数体内部的修改影响不了外部的值,也就是形参无法修改实参
    cout << t << endl;

    return 0;
}

lambda按引用捕获

// lambda按引用捕获
#include <iostream>
using namespace std;
int main()
{

    //按值捕获
    int t = 10;
    [t]()
    {
        cout << t << endl;
    }();

    auto f = [t]()
    {
        cout << t << endl;
    };
    t = 11;
    // 10 按值捕获的时候,在捕获时变量的值是多少,捕获的值就是多少,无法捕获到在捕获后面才声明的值
    f();

    //按引用捕获,传的是指针,可以改变实参
    auto f2 = [&t]()
    {
        t = 13;
    };
    t = 11;
    f2();
    // 13 按引用捕获可以捕获到在函数后面才声明的值,并可以改变它,在函数调用前变量的值是多少,引用捕获的值就是多少
    //在函数内部修改按引用捕获的值后就可以修改外部变量的值,也就是通过指针改变实参
    cout << "&t = " << t << endl;

    //捕获列表,可以同时捕获多个变量
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;
    [a, b]()
    {
        cout << "a = " << a << " b= " << b << endl;
    }();

    //捕获外部所有的变量,按值捕获
    [=]()
    {
        cout << "a = " << a << " b= " << b << " c = " << c << " d= " << d << " t= " << t << endl;
    }();

    //捕获外部所有的变量,按引用捕获
    [&]()
    {
        cout << "&a = " << a++ << " &b= " << ++b << " &c = " << c++ << " &d= " << d++ << " &t= " << ++t << endl;
    }();

    //既按值捕获,又按引用捕获
    [a, b, &c, d, &t]()
    {
        cout << "a = " << a << " b= " << b << " &c = " << c++ << " d= " << d << " &t= " << ++t << endl;
    }();
    return 0;
}

lambda实战

// lambda实战
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void test01()
{
    //使用动态数组遍历判断奇偶数的普通写法
    vector<int> v = {1, 2, 3, 4, 5};
    for (int i = 0; i < v.size(); i++)
    {
        if (v[i] % 2 == 0)
        {
            cout << v[i] << "是偶数" << endl;
        }
        else
        {
            cout << v[i] << "是奇数" << endl;
        }
    }

    cout<<"-------------------------------"<<endl;
    //使用lambda的写法,在for_each的最后面使用匿名函数遍历
    //在每一次遍历完后将元素传递给参数列表的n
    for_each(v.begin(), v.end(), [](int n) {
         if (n % 2 == 0)
        {
            cout << n << "是偶数" << endl;
        }
        else
        {
            cout << n << "是奇数" << endl;
        }
    });
}
int main()
{
    test01();
    return 0;
}

lambda-function函数对象包装器

// lambda-function函数对象包装器
#include <iostream>
using namespace std;
//需要包含这个头文件
#include <functional>
int test01(int n)
{
    cout << n << endl;
    return n;
}
class CTest
{
public:
    //构造函数
    CTest() {}

    //普通成员函数
    int Mytest(int n)
    {
        cout << n << endl;
        return n;
    }
    //仿函数的写法
    int operator()(int n)
    {
        cout << n << endl;
        return n;
    }
};
int main()
{
    //普通函数
    test01(1);

    //函数对象包装器,第一个int是返回值的类型,第二个()里的int是参数列表
    function<int(int)> f = test01;

    //将普通函数包装成一个叫做f的对象,可以直接使用f进行调用
    f(123);

    //支持4种函数的包装:1.普通函数 2.匿名函数 3.类的成员函数 4.仿函数(重载了()运算符的函数)

    //匿名函数包装
    function<int(int)> f2 = [](int n) -> int
    {
        cout << n << endl;
        return n;
    };
    f2(456);

    //类的成员函数包装 因为类的成员函数传递的是类的对象的引用,所以在参数列表里要写上类的指针
    function<int(CTest *, int)> f3 = &CTest::Mytest;
    CTest t;
    //调用包装的函数
    f3(&t, 789);

    //仿函数的包装
    function<int(CTest *, int)> f4 = &CTest::operator();

    //调用仿函数
    f4(&t, 123);
    return 0;
}

lambda-bind机制

// bind机制
#include <iostream>
#include <functional>
using namespace std;
void test01(int a, int b, int c, int d)
{
    cout << a << " " << b << " " << c << " " << d << endl;
}
int main()
{

    //假设有一个函数有很多的参数,而每次调用的时候都想传递1 2 3 4 给参数 a b c d
    //那么就可以利用bind把调用的过程用参数和函数名绑定变成一个对象
    auto a = bind(test01, 1, 2, 3, 4);

    //那么在下次调用时就可以直接通过调用对象的方式调用绑定的函数,函数默认传参
    a();

    //假设在绑定时第2个函数的参数暂时不知道,可以用placeholders使参数待定,等待调用的时候再根据传递的值进行绑定
    //_1代表第一个参数,也就是调用的时候先传的第一个参数
    auto foo2 = bind(test01, 1, placeholders::_1, 3, 4);
    //这里的2是传给了placeholders::_1
    foo2(2);

    //使多个参数待定
    auto foo3 = bind(test01, 1, placeholders::_2, placeholders::_1, 4);
    //这里的1是传给了placeholders::_1, 2是传给了placeholders::_2
    //这样的话就可以使函数调用的顺序不同
    foo3(1, 2);
    return 0;
}