基础篇:Lambda 表达式和函数对象 您所在的位置:网站首页 调用lambda函数的使用 基础篇:Lambda 表达式和函数对象

基础篇:Lambda 表达式和函数对象

#基础篇:Lambda 表达式和函数对象| 来源: 网络整理| 查看: 265

以下文章给出了很棒的总结,我如下的总结主要来源于Microsoft的C++文档和欧长坤的书。

目录:

Lambda表达式

- 捕获列表

- 值捕获

- 引用捕获

- this指针

- 泛型捕获

- 参数列表

- Mutable 规范

- Lambda函数体

constexpr Lambda 表达式函数对象Lambda 表达式

Lambda 表达式是现代 C++ 中最重要的特性之一,而 Lambda 表达式,实际上就是提供了一个类似匿名函数的特性, 而匿名函数则是在需要一个函数,但是又不想费力去命名一个函数的情况下去使用的。这样的场景其实有很多很多, 所以匿名函数几乎是现代编程语言的标配。

先看一个ISO C++ Standard给出的关于std::sort()的例子:

#include #include void abssort(float* x, unsigned n) { std::sort(x, x + n, // Lambda expression begins [](float a, float b) { return (std::abs(a) 图片来源于:https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=vs-2019capture clause (Also known as the lambda-introducer in the C++ specification.)parameter list Optional. (Also known as the lambda declarator)mutable specification Optional.exception-specification Optional.trailing-return-type Optional.lambda body.

即对应如下:

[捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 { // 函数体 }

上面的语法规则除了 [捕获列表] 内的东西外,其他部分都很好理解,只是一般函数的函数名被略去, 返回值使用了一个 -> 的形式进行。

所谓捕获列表,其实可以理解为参数的一种类型,lambda 表达式内部函数体在默认情况下是不能够使用函数体外部的变量的, 这时候捕获列表可以起到传递外部数据的作用。

捕获列表 capture clause

Lambda 在C++14可以函数body里引入新的variables,它可以周围的scope中捕获variables,哪些variables能被捕获,以传值或是引用,都需要说明。

[]:默认不捕获任何变量;[=]:默认以值捕获所有变量;[&]:默认以引用捕获所有变量;[x]:仅以值捕获x,其它变量不捕获;[&x]:仅以引用捕获x,其它变量不捕获;[=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;[&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;[this]:通过引用捕获当前对象(其实是复制指针);[*this]:通过传值方式捕获当前对象;

举个例子,如果要引用捕获total,值捕获factor,下列的表达都可以实现该目标:

[&total, factor] [factor, &total] [&, factor] [factor, &] [=, &total] [&total, =]

当默认捕获使用时,仅仅在lambda里面用到的变量才会被捕获。

当捕获列表中有默认引用捕获&,不能再有其他的捕获也有&。同理,有了默认的值捕获=,就不能有其他的默认值捕获=了。即一个identifier或是this不能在捕获列表中出现两次。看下面的例子:

struct S { void f(int i); }; void S::f(int i) { [&, i]{}; // OK [&, &i]{}; // ERROR: i preceded by & when & is the default [=, this]{}; // ERROR: this when = is the default [=, *this]{ }; // OK: captures this by value. See below. [i, i]{}; // ERROR: i repeated }

std:c++17以后,this指针可以通过*this进行值捕获。通过值捕获,就意味着整个闭包(closure)被复制到lambda被调用的地方,这个闭包是封装了lambda表达式的匿名函数对象。lambda的值捕获在parallel或asynchronous操作中很有用,相应的代码可能会在原始的object离开scope后才执行,特别在特定的硬件架构中,不如NUMA。

大家可能会想lambda表达式最前面的方括号的意义何在?其实这是lambda表达式一个很要的功能,就是闭包。这里我们先讲一下lambda表达式的大致原理:每当你定义一个lambda表达式后,编译器会自动生成一个匿名类(这个类当然重载了()运算符),我们称为闭包类型(closure type)。那么在运行时,这个lambda表达式就会返回一个匿名的闭包实例,其实一个右值。所以,我们上面的lambda表达式的结果就是一个个闭包。闭包的一个强大之处是其可以通过传值或者引用的方式捕捉其封装作用域内的变量,前面的方括号就是用来定义捕捉模式以及变量,我们又将其称为lambda捕捉块。

在上面的捕获方式中,注意最好不要使用[=]和[&]默认捕获。很大可能会出现悬挂引用(Dangling references),因为引用捕获不会延长引用的变量的声明周期:

std::function add_x(int x) { return [&](int a) { return x + a; }; }

因为参数x仅是一个临时变量,函数调用后就被销毁,但是返回的lambda表达式却引用了该变量,但调用这个表达式时,引用的是一个垃圾值,所以会产生没有意义的结果。

更多的例子:Examples of Lambda Expressions

值捕获

与参数传值类似,值捕获的前提是变量可以拷贝,不同之处则在于,被捕获的变量在 lambda 表达式被创建时拷贝, 而非调用时才拷贝:

void lambda_value_capture() { int value = 1; auto copy_value = [value] { return value; }; value = 100; auto stored_value = copy_value(); std::cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有