基本语法就不谈了。
值捕获
- 值捕获就相当于在创建lambda对象时复制了一份所捕获的变量;
- 如果以传值的形式捕获外部变量,那么,lambda 体不允许修改外部变量;
- 可以使用mutable关键字打破2的限制: mutable { ... }
- 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; } // 使用拷贝
);