lambda 表达式在 C++11 之后的版本中是一种定义匿名函数的简洁语法。在一些长环境下,例如 STL 算法和异步操作,使用 lambda 作为回调函数非常自然和方便,并且 lambda 和 C++ 的函数式编程特性非常契合。
基本语法#
一个完整的 lambda 表达式语法如下:
[caputreList](paramList) mutable throw() -> returnType {
// do something..
}
捕获列表#
lambda 以一个中括号的捕获列表开始,捕获列表用来定义哪些外部变量可以被 lambda 表达式使用,以及如何使用(传值,引用)。
捕获列表可以为空,表示 lambda 不使用闭包之外的变量,但中括号不可省略。
无论是传值还是引用方式,都可以使用默认的捕获方式,在默认捕获方式下,编译器会自动分析 lambda 需要捕获哪些外部变量。默认传值捕获方式和默认的引用捕获方式可以混用,具体用法如下:
[=] // 默认传值捕获
[&] // 默认引用捕获
[&, factor] // 除factor传值捕获外,其他变量使用引用捕获。
[=. factor] // 除factor引用捕获外,其他变量使用传值捕获。
捕获列表不允许重复声明,这种重复包括捕获方式重复和变量重复:
[&, &factor] // 错误, & 已经声明了默认的捕获方式. &factor 重复
[=, factor] // 同上
[factor, factor] // 错误,变量重复
自动捕获提供了更方便的捕获机制,但是可能会导致无意间捕获不需要的变量,可能会到之前在的内存使用增加或引入性能问题。
传值还是引用捕获的选择一般需要通过特定的场景来选择,一般来说,传值还是引用的捕获方式选择和正常函数传参方式的规则差不多。
捕获列表同样可以使用可变参数模板:
[arg...]
在类成员函数体中使用 lambda 表达式时,需要把this
指针显式捕获,以提供对类成员函数和数据的访问权限。
[this, filter]
C++14 中引入了一种新的默认捕获方式,可以在捕获字句中引入并初始化新的变量,而无需把这些变量放在闭包内,这对于一些需要考虑作用域和声明周期的变量非常有用:
auto p = make_uniqure<T>(factor);
auto _lambda = [ptr = move(p)]{}
参数列表#
lambda 的参数列表行为等大多数方面类似于标准函数的参数列表。
捕获列表和参数列表在某种意义上都可以用来给闭包提供变量,但捕获列表适用于哪些在定义 lambda 时就已知且在后续调用中不会改变的变量。
捕获列表适用于在整个 lambda 的声明周期中保持不变的变量,参数列表适用于在每次调用 lambda 时都不同的变量。
std::vector<int> num = {1,2,3,4};
int fixedValue = 10;
std::for_each(num.begin(). num.end(), [fixedValue](int &x){x += fixedValue;});
在参数列表中,如果参数类型是泛型的,则使用 auto 告知编译器创建模板:
[](auto factor)
mutable#
一般地,lambda 的函数调用时 const-by-value 的,但是加入 mutable 规范后,闭包可以修改值捕获的变量:
异常#
throw()
用来指示 lambda 抛出异常,可以省略,也可以使用 noexcept 指示不抛出异常:
[]() noexcpet {}
[]() throw() {}
[]() {}
返回类型#
在 lambda 中 返回类型指示可以省略,由编译器自动推断,但也有例外:
// C++11以及以前,某些编译器
[](int i){
if (i < 0){
return i;
}
else{
return -i;
}
} // 错误,推断类型为void
这是由于闭包的operator()
推断规则:
如果主体只包含 return 一个表达式,返回类型为表达式类型,否则返回类型为 void。 (C++ 14 之前)。
lambda 体#
基本行为和一般函数一致。