C++11的lambda表达式支持捕获外部作用域的变量,当lambda定义在类的非静态成员函数中时,捕获this指针可以让lambda内部访问当前对象的成员变量和成员函数,这是类内使用lambda的常见场景。

基本捕获方式
在类的成员函数中,lambda可以通过值捕获或者引用捕获的方式获取this指针,两种方式的语法和效果有一定区别。
值捕获this
值捕获this时,lambda会复制当前对象的this指针,语法上可以直接写[this],也可以将this放在值捕获列表中。捕获后lambda内部可以通过this访问对象的成员。
#include <iostream>
#include <functional>
class Test {
public:
int value = 10;
std::function<int()> getLambda() {
// 值捕获this
return [this]() {
// 通过this访问成员变量
return this->value;
};
}
};
int main() {
Test t;
auto func = t.getLambda();
std::cout << func() << std::endl; // 输出10
return 0;
}
隐式捕获this
如果使用[=]或者[&]的隐式捕获方式,lambda也会自动捕获this指针,不需要显式写出this。其中[=]会隐式值捕获this,[&]会隐式引用捕获this。
#include <iostream>
#include <functional>
class Test {
public:
int value = 20;
std::function<int()> getLambdaByEqual() {
// [=]隐式值捕获this
return [=]() {
return value; // 等价于this->value
};
}
std::function<int()> getLambdaByRef() {
// [&]隐式引用捕获this
return [&]() {
return value; // 等价于this->value
};
}
};
int main() {
Test t;
auto func1 = t.getLambdaByEqual();
auto func2 = t.getLambdaByRef();
std::cout << func1() << std::endl; // 输出20
std::cout << func2() << std::endl; // 输出20
return 0;
}
捕获this的注意事项
虽然捕获this可以让lambda方便访问对象成员,但需要注意this指针的生命周期问题,避免产生悬垂指针。
悬垂指针风险
如果lambda的生命周期超过了捕获this对应的对象生命周期,那么lambda内部的this就会变成悬垂指针,访问成员时会产生未定义行为。
#include <iostream>
#include <functional>
class Test {
public:
int value = 30;
std::function<int()> getLambda() {
return [this]() {
return this->value;
};
}
};
int main() {
std::function<int()> func;
{
Test t;
func = t.getLambda();
} // t在这里被销毁,this变成悬垂指针
// 下面调用会产生未定义行为
// std::cout << func() << std::endl;
return 0;
}
为了避免这个问题,可以在捕获时复制需要的成员变量,而不是捕获this,这样lambda的生命周期就不依赖原对象。
#include <iostream>
#include <functional>
class Test {
public:
int value = 40;
std::function<int()> getLambda() {
// 复制成员变量,不捕获this
int temp = value;
return [temp]() {
return temp;
};
}
};
int main() {
std::function<int()> func;
{
Test t;
func = t.getLambda();
} // t销毁后,lambda内部的temp是独立副本,不受影响
std::cout << func() << std::endl; // 输出40
return 0;
}
捕获this与mutable关键字
如果lambda被声明为mutable,那么值捕获的this指针本身不能被修改,但是通过this修改成员变量是允许的,因为this指向的对象不是lambda的内部状态。
#include <iostream>
#include <functional>
class Test {
public:
int value = 50;
std::function<void()> getLambda() {
return [this]() mutable {
// 可以修改成员变量
this->value = 100;
// 不能修改this本身,下面代码会编译报错
// this = nullptr;
};
}
};
int main() {
Test t;
auto func = t.getLambda();
func();
std::cout << t.value << std::endl; // 输出100
return 0;
}
常见使用场景
捕获this的lambda常用于以下场景:
- 在类的成员函数中定义回调,需要访问当前对象的成员
- 将lambda作为任务传递给线程或者异步函数,需要操作对象状态
- 在STL算法中结合对象成员使用,比如
std::sort中需要比较对象的成员变量
使用时要始终注意lambda的生命周期和对象生命周期的匹配,避免悬垂指针问题。