C++ Lambda 表达式详解

Lambda 表达式是C++11 引入的一种强大的匿名函数定义方式,它使得函数式编程在 C++ 中变得简单易用。尽管对于许多其他语言来说,Lambda 表达式并不是什么新鲜事物,但对于 C++ 程序员来说,它的语法可能一开始会显得有些繁琐。本文旨在帮助读者理解并掌握 C++ Lambda 表达式的使用。

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 表达式可以被命名,以便在稍后的时间点使用。只有匿名 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表达式也可以返回值。返回值在圆括号之后和花括号之前提供。

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 表达式的独特属性之一,它帮助 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 捕获列表仅适用于局部作用域,不能用于全局变量。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485