C++ lambda

最简单的lambda:

int main()
{
    auto lambda = []{};
    lambda();
}

让lambda更有用,就要捕捉局部变量。

#include <stdio.h>
int main()
{
    int x = 1;
    
    auto lambda = [x] { 
        printf("x=%d",x); 
    }; 

    lambda();
}

[x]是按值方式的捕捉,而且lambda对应的那个可调用的方法,是const。可以脑补这个等价物:

#include <stdio.h>
int main()
{
    int x=1;
    
    class unnamed_functor{
    public:
        unnamed_functor(int arg): x{arg}{}
        void operator()() const{
// x=2; 错误,const函数内不能修改成员变量 printf(
"x=%d",x); } int x; }; unnamed_functor lambda{x}; lambda(); }

可以使用mutable重新标记lambda函数,这个标记会清除const标记。例如:

#include <stdio.h>
int main()
{
    int x = 1;
    
    auto lambda = [x] mutable{ 
        x=2;
        printf("in lambda x=%d\n",x); 
    }; 

    lambda();

    printf("x=%d\n",x); 
}

[&y]是按照引用方式捕捉局部变量。例如:

#include <stdio.h>
int main()
{
    int x=1, y= 2;
    
    auto lambda = [x, &y] { 
        y=3;
        printf("in lambda y=%d\n",y); 
    }; 

    lambda();

    printf("y=%d\n",y); 

}

可见,按照引用方式捕捉的局部变量,不用标记mutable也能更改y值。脑补如下伪代码:

#include <stdio.h>
int main()
{
    int x=1,y=2 ;
    
    class unnamed_functor{
    public:
        unnamed_functor(int x_, int& y_): x{x_},y(y_){}
        void operator()() const{
            // x=2; 错误,const函数内不能修改成员变量
            y=3;
            printf("in lambda y=%d\n",y);  
        }
        int x;
        int& y;
    }; 
    
    unnamed_functor lambda(x,y);
    lambda();

    printf("y=%d\n",y); 
}

这里有个惊人的知识盲点,const成员函数能修改引用数据成员。仔细分析一下,const成员函数,关键意图是不更改对象的状态。对于int& y数据成员,就是不能更改此引用指向的对象。
当检查y=3时,并没有改变y所指向的对象(引用的本质是指针或者对象地址),因此与const不矛盾。这就解释了lambda不用加mutable就能搞定引用捕捉的局部变量的修改问题。

lambda作为一个Callable,可以接受任意调用参数:

int main(){
    int x=1;
    auto lambda = [x](int arg){ return x+arg; }
    std::cout << lambda(2); // show 3
}

C++14开始,lambda能有自己的内部状态变量了。从前只有两个状态来源:[]捕捉的局部变量( automatic storage duaration 不能是全局变量)()捕捉的调用参数。
例如:

#include <stdio.h>

auto generator = [x = 0] () mutable {
  return x++;
};

int main()
{
    auto a = generator(); // == 0
    auto b = generator(); // == 1
    auto c = generator(); // == 2
}

脑补一下这个东西的等价物:

#include <stdio.h>

class generator{
public:
    generator():x(0){}
    int operator()(){
        return x++;
    }
    int x;
};

int main()
{
    auto a = generator(); // == 0
    auto b = generator(); // == 1
    auto c = generator(); // == 2
}

lambda捕捉能力进一步增强,可以捕捉任意的表达式。考虑如下问题:

#include <stdio.h>
#include <memory>

int main()
{
    auto p = std::make_unique<int>(1);
    //auto q = p; error:不能copy
    //auto k = std::move(p); ok: 能move copy
    
    auto lambda = [p] { 
        //...
    };
}

显然,传值方式失败了,因为unique_ptr不支持copy。为此,使用C++14的新语法:

#include <stdio.h>
#include <memory>

int main()
{
    auto p = std::make_unique<int>(1);
    //auto q = p; error:不能copy
    //auto k = std::move(p); ok: 能move copy
    auto lambda = [k=std::move(p)] { 
        //...
    };
}

lambda捕捉this指针的语法:

#include <stdio.h>
#include <memory>

struct MyObj {
  
    int value{ 123 };
    MyObj() = default;
    MyObj(const MyObj& other){
        value = other.value;
        printf("MyObj(const MyObj& other) \n");
    }
    
    auto getValueCopy() {
      return [*this] { return value; };
    }
    auto getValueRef() {
       return [this] { value=111; return value; };
    }
};

int main()
{

    MyObj mo;
    auto valueCopy = mo.getValueCopy();
    auto valueRef = mo.getValueRef();
    mo.value = 321;
    valueCopy(); // 123
    valueRef(); // 321
    
    printf("%d\n", mo.value);
    
}

参考:https://github.com/AnthonyCalandra/modern-cpp-features#lambda-capture-this-by-value

posted @ 2018-04-05 12:08  thomas76  阅读(200)  评论(0编辑  收藏  举报