美文网首页
关于lambda表达式

关于lambda表达式

作者: 丑角的晨歌 | 来源:发表于2018-09-26 21:00 被阅读0次

    基本语法就不谈了。

    值捕获

    1. 值捕获就相当于在创建lambda对象时复制了一份所捕获的变量;
    2. 如果以传值的形式捕获外部变量,那么,lambda 体不允许修改外部变量;
    3. 可以使用mutable关键字打破2的限制: mutable { ... }
    4. this只能按值捕获,且访问this的成员不必使用this->语法,可以直接访问;
    int a = 0;
    auto test = [a]()  {
        cout << a << endl;
    };
    test();  // 0
    
    a = 1;
    test();  // 0
    

    虽然可以用mutable让lambda中可以修改按值捕获的变量,但mutable也有它的问题,使用时要注意:

    int a = 0;
    auto test = [a]() mutable {
        cout << a << endl;
        a = 3;
    };
    test();  // 0
    test();  // 3,这里a被修改过
    

    引用捕获
    引用捕获相当于函数传参时传了个引用,所以一定要注意变量生存期的问题;

    int a = 0;
    auto test = [&a]()  {
        cout << a << endl;
    };
    test();  // 0
    
    a = 1;
    test();  // 1
    

    类的局部静态对象捕获是采用的引用方式捕获的,即使你使用了 [=] 来指定按值的方式捕获。

    class test {
      public:
       static int x;
       void testtest() {
        auto test = [=]() {
            cout << x << endl;  // 注意x是类的static变量
            x = 5;
         };
         test();  // 0
         test();  // 5, 值已经在上一次执行时被修改了
       }
    };
    int test::x = 0;
    

    Effective Modern C++ 条款31 对于lambda表达式,避免使用隐式捕获模式
    鉴于对this、static类型变量捕获时的特殊情况,在Effective Modern C++中建议我们不要使用隐式捕获:隐式的引用捕获模式可能会导致悬挂引用,而使用隐式的值捕获模式诱骗你——让你认为你可以免疫刚说的问题(事实上没有免疫),然后它又骗你——让你认为你的闭包是独立的(事实上它们可能不是独立的)。
    比如下面的例子:

    class Widget {
    public:
        ...          // 构造函数等
        void addFilter() const;  // 添加一个条目
    
    private:
        int divisor;         // 用于Widget的过滤器中
    };
    
    void Widget::addFilter() const
    {
        filters.emplace_back(
          [=](int value) { return value % divisor == 0; }
        );
    }
    

    可能你会认为,默认的值捕获使divisor已经被拷贝到闭包里,但其实不然,这里divisor是Widget类的成员,所以实际上捕获的是this,闭包内对divisor的使用会被编译器替换为this->divisor,所以就出现了闭包的生命周期可能会超出Widget实例的生存期。
    所以如果根据这条建议,当我们写清楚每个变量的捕获方式,在这里试图用值捕获divisor时,编译器就会阻止我们,这有助于在更早的阶段发现问题。
    那么这里的话,如果确实想捕获divisor的一个拷贝,可以这么写:

    auto divisorCopy = divisor;          // 拷贝成员变量
    
    filters.emplace_back(
      [=](int value)                // 捕获拷贝
      { return value % divisorCopy == 0; } //使用拷贝
    );
    

    如果在C++14里,还支持广义lambda捕获,即捕获的可以是表达式:

    filters.emplace_back(               // C++14
      [divisor = divisor](int value)    // 在闭包中拷贝divisor
      { return value % divisor == 0; }  // 使用拷贝
    );

    相关文章

      网友评论

          本文标题:关于lambda表达式

          本文链接:https://www.haomeiwen.com/subject/sswloftx.html