捕获是指一个lambda表达式将局部变量包含在捕获列表,在捕获列表中的参数则可以被lambda函数体使用。举一个最简单的例子:
std::string str("this is captured value");
auto f = [str] {std::cout << str << std::endl;};
在例子中我们可以看出,通过捕获局部变量,lambda表达式可以在函数体重使用该变量,和参数传递的效果一样。但在某些场合中,lambda捕获则表现出巨大的作用,比如当lambda表达式作为谓词使用在STL的算法函数中的时候。比如实现一个函数,将一个vector < int >中的大于某一个数的修改为0。
#include <algorithm> // std::transform
#include <iostream>
std::vector<int>& modifyVector(std::vector<int>& data,int upperBound)
{
auto modifyFunc= [upperBound](int item) ->int { return (item >= upperBound) ? 0:item; };
std::transform(data.begin(), data.end(), data.begin(), modifyFunc);
return data;
}
void printVector(const std::vector<int>& data)
{
for(auto item = data.cbegin();item != data.cend();item++)
{
std::cout << *item << " ";
}
std::cout << std::endl;
}
int main()
{
std::vector<int> data{ 6,4,3,4,5,7,8,7,6,6,5 };
std::cout << "original data: ";
printVector(data);
modifyVector(data, 7);
std::cout << "modified data: ";
printVector(data);
return 0;
}
运行结果如下:
lambda捕获有两种捕获方式,分别是值捕获和引用捕获。
值捕获就像前面例子那样,与传值参数类似,采用值捕获的前提是变量可以拷贝复制,与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝,比如:
int a = 30;
auto f = [a] {return a;};
a = 0;
auto b = f(); //b = 30
由于被捕获的变量的值是在lambda创建时复制的,所以随后对其的修改并不会影响到lambda内对应的值。
lambda也可以采用引用捕获变量,其工作方式和引用相同。例如:
int a = 30;
auto f = [&a] {return a;};
a = 0;
auto b = f(); //b = 0
引用捕获和返回引用有相同的问题和限制,如果我们采用引用方式捕获一个变量,就必须确保被引用对象在lambda执行时时存在的。所以一般建议保持lambda变量捕获简单化,对于普通变量,如int,float和string等类型,通常采用值捕获。对于捕获指正,迭代器或采用引用捕获,就必须要保证其在lambda执行时,迭代器、指正和引用的对象依然存在,且有预期值。
以上捕获为显示捕获,除了显示列出我们希望使用的变量外,还可以使用隐式捕获,即让编译器自己来推断我们需要捕获什么变量。我们在捕获列表中使用=或&,=表示采用值捕获,&表示引用捕获。例如:
int func(int v1, int v2)
{
auto func = [=] {return v1+v2;};
return func();
}
int func2(int& v1,int& v2)
{
auto f = [&] {return v1+v2;};
return f();
}
int main()
{
auto v = func(2,3); //v = 5
int a1 = 3;
int a2 = 4;
v = func2(a1,a2); //v = 7
return 0;
}