Lambda 表达式是C++11 引入的一种强大的匿名函数定义方式,它使得函数式编程在 C++ 中变得简单易用。尽管对于许多其他语言来说,Lambda 表达式并不是什么新鲜事物,但对于 C++ 程序员来说,它的语法可能一开始会显得有些繁琐。本文旨在帮助读者理解并掌握 C++ Lambda 表达式的使用。
Lambda 表达式的语法由三部分组成:
下面是一个简单的 Lambda 表达式示例:
int main() {
[]{}();
return 0;
}
虽然一开始看起来有些奇怪,但会在后面讨论捕获列表和参数列表。首先,让理解如何使 Lambda 表达式具有功能性。
int main() {
[]{
std::cout << "Lambda calling this cout" << std::endl;
}();
return 0;
}
Lambda 表达式具有功能性,但它不会自动调用自己,需要显式地调用它。Lambda 表达式的调用有两种方式:
就地调用类似于调用任何其他函数:
int main() {
[]{
std::cout << "Lambda calling this cout" << std::endl;
}();
return 0;
}
同样,函数参数可以在 Lambda 表达式中通过在参数列表中指定这些参数来传递。记住,在就地调用时,需要提供参数,否则会产生编译错误。
int main() {
[](int val) {
std::cout << "The value passed in this function is ->" << val << std::endl;
}(100);
return 0;
}
Lambda 表达式可以被命名,以便在稍后的时间点使用。只有匿名 Lambda 表达式需要像第1节中描述的那样就地使用。
有三种可能的方式给 Lambda 表达式命名:
Lambda 表达式可以通过使用C++auto 关键字简单地分配一个名称。到目前为止,这是给 Lambda 表达式分配名称的最简单方式。
int main() {
auto lfn = [](){
std::cout << "This lambda is called using the names" << std::endl;
};
lfn();
return 0;
}
auto 关键字自动生成函数指针。然而,可以显式使用 std::function<> 来持有那个函数指针。下面的代码片段显示了 std::function<> 的用法。
int main() {
std::function lfnfunc = [](int val) {
std::cout << "The value passed in this function is ->" << val << std::endl;
};
lfnfunc(200);
return 0;
}
Lambda 表达式只是函数,可以被分配给 'C' 风格的函数指针。尽管强烈不推荐使用这种方式:
int main() {
void (*cstylefp)(int) = [](int val) {
std::cout << "The value passed in this function is ->" << val << std::endl;
};
cstylefp(300);
return 0;
}
就像普通函数一样,Lambda表达式也可以返回值。返回值在圆括号之后和花括号之前提供。
Lambda表达式中使用箭头 (->) 符号来指定返回类型。
int main() {
int retval = []()->int {
return (int)1;
}();
return 0;
}
就像上面的例子一样,也可以给带有返回类型的 Lambda 表达式命名。如上所述,使用 auto 关键字是最佳方式。
int main() {
auto lfnParam = [](int val) -> int {
std::cout << "lambda takes and integer ->" << val << "<- and returns an Integer" << std::endl;
return val * 100;
};
int retval1 = lfnParam(100);
return 0;
}
捕获列表是 Lambda 表达式的独特属性之一,它帮助 Lambda 表达式像 JavaScript 等其他语言中的闭包一样工作。
捕获列表主要用于将局部变量传递给 Lambda 函数,而不需要显式地将它们作为参数列表的一部分。
参数可以通过值、引用或两者的组合来传递。
局部变量可以通过在捕获列表中指定它们来传递到 Lambda 函数。然后可以在 Lambda 函数内部访问这些变量。
int main() {
int x = 10, y = 20;
auto retVal = [x, y]() -> int {
return x + y;
}();
std::cout << "retVal => " << retVal << " x => " << x << " y => " << y << std::endl;
return 0;
}
局部变量也可以通过引用在捕获列表中传递。如果通过引用传递,对局部变量的更新将反映在 Lambda 函数外部。
int main() {
int x = 10, y = 20;
auto retVal1 = [&x, &y]() -> int {
x++; y++;
return x + y;
}();
std::cout << "retVal1 => " << retVal1 << " x => " << x << " y => " << y << std::endl;
return 0;
}
在需要一次性通过值传递所有局部变量的情况下,可以使用捕获列表中的 '=' 符号来实现,如下所示:
int main() {
int x = 10, y = 20, z = 30;
auto retval2 = [=]() -> int {
return x + y + z;
}();
std::cout << "retVal2 =>" << retval2 << std::endl;
return 0;
}
在需要一次性通过引用传递所有局部变量的情况下,可以使用捕获列表中的 '&' 符号来实现,如下所示:
int main() {
int x = 10, y = 20, z = 30;
auto retval3 = [&]() -> int {
x++; y++; z++;
return x + y + z;
}();
std::cout << "retVal3 =>" << retval3 << " x => " << x << " y =>" << y << " z=> " << z << std::endl;
return 0;
}
变量可以在捕获列表中以混合和匹配的方式传递,其中一些变量通过值传递,一些通过引用传递,如下所示:
int main() {
int x = 10, y = 20;
auto retval4 = [x, &y]() -> int {
y++;
return x + y;
}();
std::cout << "retVal4 =>" << retval4 << " x => " << x << " y =>" << y << std::endl;
return 0;
}
注意1:不能修改通过值传递的变量,即不能在上述示例中执行 x++。
注意2:Lambda 捕获列表仅适用于局部作用域,不能用于全局变量。