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