lambda可以大幅度简化代码,消除因编写可调用对象产生的公式化代码。lambda函数能访问外部调用语境中的本地变量,便捷地表达出自身语义,无须另行设计带有函数调用操作符的类,再借成员变量捕获状态。
lambda通常用于生成匿名的可调用对象,[]内为捕获列表,可以指定捕获方式,()内表示接收的参数,{}内为函数体,以下代码将vector中的元素写到std::cout中:
std::vector<int> data = load_data();//假设load_data()函数获取数据
std::for_each(data.begin(), data.end(), [](int i) {std::cout << i << std::endl; });
若函数体无法仅用一条return语句写成,就需要明确设定返回值,方法是在()后附上箭头(->)和目标返回值型别:
std::condition_variable cond;//条件变量
bool data_ready;
void wait_for_data()
{
std::unique_lock<std::mutex> lk(m);
//只有data_ready为true时,wait()的调用才会结束并返回
cond.wait(lk, []()->bool {
if (data_ready)
{
std::cout << "Data ready!" << std::endl;
return true;
}
else
{
std::cout << "Data not ready, resuming wait!" << std::endl;
return false;
}
})
}
捕获本地变量的lambda函数
若要在lambda函数中访问本地变量,则需先将其捕获,最简单的方式是使用引导符“[=]”,即可访问本地变量的副本:
#include<functional>
std::function<int(int)> offseter(int offset)
{
return [=](int j) {return offset + j; };
}
int main()
{
std::function<int(int)> offset_52 = offseter(52);
std::cout << offset_52(10) << std::endl;//返回62
}
除了上述方式捕获本地变量,还可以以引用方式进行捕获“[&]” ,照此处理,一旦lambda函数脱离生成函数或所属代码块的作用域,引用变量就会被销毁,此时若仍调用lambda则会引发未定义行为。
int main()
{
int offset = 28;
std::function<int(int)> offset_a = [&](int i) {return offset + i; };
offset = 50;
std::function<int(int)> offset_b = [&](int i) {return offset + i; };
std::cout << offset_a(50) << "," << offset_b(50) << std::endl;
//输出100, 100
}
若想按复制方式捕获大部分本地变量,针对一两个变量采用索引方式捕获,则应该使用形如“[=]”的lambda引导符,在等号后面逐一列出引用变量:
int main()
{
int i = 123, j = 456, k = 789;
std::function<int()> f = [=, &j, &k] {return i + j + k; };
i = 1;
j = 2;
k = 3;
std::cout << f() << std::endl;
//输出128(123+2+3)
}
使用[&, j, k]则以复制方式捕获j,k,以引用方式捕获其余变量:
int main()
{
int i = 123, j = 456, k = 789;
std::function<int()> f = [&, j, k] {return i + j + k; };
i = 1;
j = 2;
k = 3;
std::cout << f() << std::endl;
//输出1246(256+789+1)
}
若要在lambda函数内部访问类的数据成员,则必须在捕获列表上加上this指针以捕获之:
struct C
{
int some_data;
void do_something(std::vector<int>&vec)
{
std::for_each(vec.begin(), vec.end(),
[this](int& i) {i += some_data; });
}
};
上述例子使用this捕获数据成员some_data。
C++14开始,lambda也能有泛型形式,其中的参数被声明为auto,而非具体型别,根据运行时外部提供的参数导出:
auto f = [](auto x) {std::cout << "x=" << std::endl; };
f(50);
f("hello!");
C++14还加入了广义捕获(generalized capture)的概念,因此能够捕获表达式的运算结果,而不再局限于直接复制或引用本地变量,该特性用于以移动方式捕获只移型别:
std::future<int> spawn_task()
{
std::promise<int> p;
auto f = p.get_future();
std::thread t([p = std::move(p)](){p.set_value(find_the_answer();)});
t.detach();
return f;
}
p=std::move(p)就是广义捕获行为,将promise实例移入lambda函数,因此线程可以安全地分离,不必担心因为本地变量被销毁形成悬空引用。