• C++ Primer 笔记——lambda表达式


    1.一个lambda表达式表示一个可调用的代码单元,可以理解为一个未命名的内联函数,但是与函数不同,lambda表达式可能定义在函数内部。其形式如下:

    [capture list] (parameter list) -> return type { function body }

    • capture list 是一个lambda所在函数中定义的局部变量的列表(通常为空)
    • return type, parameter list 和 function body与任何普通函数一样,分别表示返回类型,参数列表和函数体
    • lambda必须使用尾置返回
    • 我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体
    • 如果忽略返回类型,lambda会从函数体推断出返回类型。
    • 如果lambda的函数体包含任何单一return语句之外的内容,且未指定返回类型,则返回void
    • 捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和在它所在函数之外声明的名字。
    void test()
    {
        int i = 1;
        int j = 2;
        auto f = [i, j](int base) -> int { return base + i + j; };    // i,j必须在捕获列表里面,这里的返回类型int其实可以省略
        int k = f(0);    // k的结果为3
    }

    2.当定义一个lambda时,编译器生成一个与lambda对应的新的(未命名的)类类型。默认情况下这个类都包含一个对于该lambda所捕获的变量的数据成员。类似任何普通类的数据成员,lambda的数据成员也在lambda对象创建时被初始化。

    3.与传值参数类似,采用值捕获的前提是变量可以拷贝,与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝。

    void test()
    {
        int i = 10;
        auto f = [i] {return i + 1; };    // 此时i的值就已经被拷贝了
        int j = f();
    }

    4.当以引用方式捕获一个变量时,必须保证在lambda执行时变量是存在的。

    void test()
    {
        int i = 10;
        auto f = [&i] {return ++i; };
        int j = f();    // 此时i等于11
    }

    5.在捕获列表中写一个 &或= 可以告诉编译器我们想采用值捕获还是引用捕获,这种方法叫做隐式捕获。

    void test()
    {
        int i = 10;
        auto f = [&] {return ++i; };    // 采用引用捕获
        int j = f();    
    }


    6.我们也可以混合使用值捕获或者引用捕获。当使用混合方式的时候,显示捕获的方式不可以与隐式捕获的方式相同。

    void test()
    {
        int i = 10;
        int j = 9;
        auto f = [=, &i] {i++; return i + j; };    // i采用引用捕获,其他采用值捕获
        int k = f();    // k等于20
    }

    7.默认情况下,对于一个值被拷贝的变量,lambda不会改变其值,如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上mutable。

    void test()
    {
        int i = 10;
        auto f = [i] (){return ++i; };    // 错误,不可以改变i的值
        auto f1 = [i] () mutable {return ++i; };    // 正确
    }

    8.bind函数(头文件 functional中)可以看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。调用bind的一般形式为:

    auto newCallable = bind(callable, arg);

    void test(int i, int j)
    {
        std::cout << i - j << std::endl;
    }
    
    auto test0 = std::bind(test, 5, 1);
    test0();    // 结果4
    
    auto test1 = std::bind(test, std::placeholders::_1, 5, std::placeholders::_2, 1);    // 错误,不可以这么写
    test1();    
    
    auto test2 = std::bind(test, std::placeholders::_1, 5);
    test2(1);    // 结果-4
    
    auto test3 = std::bind(test, std::placeholders::_1, std::placeholders::_2);
    test3(3, 2);    // 结果1
    
    auto test4 = std::bind(test, std::placeholders::_2, std::placeholders::_1);    // 参数顺序可以重排
    test4(3, 2);    // 结果-1
    
    auto test5 = std::bind(test, 1, std::placeholders::_1);        // 注意这个占位符占的是test5()的参数位置,而不是test()的
    test5(5);    // 结果-4

    以上代码请注意命名空间,否则会和socket的bind函数产生二义性。

    9.如果我们想绑定的参数无法拷贝,或者我们想引用时应该使用ref函数。

    void test(int& i, int j)
    {
        std::cout << i - j << std::endl;
    }
    
    int i = 5;
    auto test0 = std::bind(test, std::ref(i), 1);
    test0();    // 结果4

    10.编译器将lambda表达式翻译成一个未命名类的未命名对象,在lambda表达式产生的类中含有一个重载的函数调用运算符。这个类不含默认构造函数,赋值运算符及默认析构函数。它是否有默认的拷贝/移动构造函数则通常要视捕获的数据成员类型而定。

    std::size_t i;
    auto test_func = [i](const std::string& str) {return str.size() > i; };
    
    class test
    {
    public:
        test(std::size_t size) : m_size(size) {}
        bool operator()(const std::string& str) { return str.size() > m_size; };
    
    private:
        std::size_t m_size;
    };


     

  • 相关阅读:
    java中将一个文件夹下所有的文件压缩成一个文件
    nodejs 指定全局安装路径和缓存路径
    webstrom 2019.2激活教程+激活工具
    CoreOnLineTransactionService.java
    CoreOnLineTransactionMapper.xml
    短信长度判断:判断是长短信
    sxnx-sms山西农信错误信息+处理方法
    Cannot format given Object as a Date
    cpu个数、核数、线程数、Java多线程关系的理解+物理cpu数和cpu核数和逻辑cpu数和vcpu区别
    如何判断短信内容是否是长短信??
  • 原文地址:https://www.cnblogs.com/zoneofmine/p/7259819.html
Copyright © 2020-2023  润新知