zoukankan      html  css  js  c++  java
  • 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;
    };


     

  • 相关阅读:
    UVa 10118 记忆化搜索 Free Candies
    CodeForces 568B DP Symmetric and Transitive
    UVa 11695 树的直径 Flight Planning
    UVa 10934 DP Dropping water balloons
    CodeForces 543D 树形DP Road Improvement
    CodeForces 570E DP Pig and Palindromes
    HDU 5396 区间DP 数学 Expression
    HDU 5402 模拟 构造 Travelling Salesman Problem
    HDU 5399 数学 Too Simple
    CodeForces 567F DP Mausoleum
  • 原文地址:https://www.cnblogs.com/zoneofmine/p/7259819.html
Copyright © 2011-2022 走看看