Lambda表达式的实质与应用

目录

Lambda表达式

lambda表达式的基本构造

Lambda表达式的本质

解析lambda表达式各部分的含义

捕获列表[ ]

形参列表( )

mutable声明

Exception异常处理  

-> 指定函数返回值类型

函数体{ }

特别注意:lambda函数返回的函数句柄

lambda函数实现一元谓词

Lambda函数实现二元谓词

在transform函数中使用Lambda函数


Lambda表达式

lambda表达式的基本构造

 

Lambda表达式的本质

lambda表达式是实现了operator(  )的匿名类(或结构)。

解析lambda表达式各部分的含义

捕获列表[ ]

捕获列表含义与格式

捕获列表标识一个 Lambda 表达式的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义 Lambda 为止时 Lambda 所在作用范围内可见的局部变量(包括 Lambda 所在类的 this)。函数对象参数有以下形式:

没有任何函数对象参数

=

函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)

&

 函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是引用传递方式(相当于是编译器自动为我们按引用传递了所有局部变量

this

函数体内可以使用 Lambda 所在类中的成员变量

a

将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是 const 的,要修改传递进来的拷贝,可以添加 mutable 修饰符

&a

将 a 按引用进行传递

a,&b

将 a 按值传递,b 按引用进行传递

=,&a,&b

除 a 和 b 按引用进行传递外,其他参数都按值进行传递

&,a,b

除 a 和 b 按值进行传递外,其他参数都按引用进行传递

代码示例

// lambda表达式.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。  
//  
  
#include <iostream>  
#include <vector>  
#include <algorithm>  
#include <functional>  
using namespace std;  
  
struct Student  
{  
    string name;  
    float mark;  
      
    Student(string name, float mark)  
    {  
        this->name = name;  
        this->mark = mark;  
    }  
  
    std::function<float(float)> LambdaExpression = [this](float AddMark)->float {return mark + AddMark; }; // 这里传入的是结构体的指针,故lambda函数体内可以使用 Lambda 所在类中的成员变量
};  
  
int main()  
{  
    Student stud1("666", 98.8);  
    float FinalMark = stud1.LambdaExpression(1);  
}  

 

其实捕获列表的含义就如同它的名字一样,“捕获作用域内所有可以使用的值”。我们看:当我们声明了一个Student实例,我们调用其中的成员函数LambdaExpression我们看到这个函数不仅仅获得了我们给他传入的实参AddMark还获得了类成员变量。这就是捕获列表的作用,它可以使用在本作用域内且在本函数外的变量的值。lambda函数体内的变量有两个来源:捕获列表和函数形参表,后者需要我们传入实际参数才可以获取,前者需要在lambda函数作用域内(lambda函数外部)进行获取。

形参列表( )

形参列表的含义与格式

形参列表的格式与含义与普通函数相同,需要我们在使用该函数时给它传入形参。

mutable声明

mutable含义

标识符

作用

mutable

当我们在形参列表后面声明了mutable标识符,我们就可以在函数体中改变捕获列表中捕获的变量。默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略。

代码示例

// lambda表达式.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。  
//  
  
#include <iostream>  
#include <vector>  
#include <algorithm>  
#include <functional>  
using namespace std;  
  
struct Student  
{  
    string name;  
    float mark;  
      
    Student(string name, float mark)  
    {  
        this->name = name;  
        this->mark = mark;  
    }  
  
    std::function<float(float)> LambdaExpression = [this](float AddMark)mutable->float {return ++mark + AddMark; }; // 改变了表达式内捕获变量的值  
};  
  
int main()  
{  
    Student stud1("666", 98.8);  
    float FinalMark = stud1.LambdaExpression(1);  
}  

 

注意:这里我说的是“改变了表达式内的捕获变量的值”,这说明“并没有改变表达式外捕获变量本身的值”,这是因为我们的捕获列表中采用的是“值传递([this])”,如果改成以下代码,就可以改变在根本上改变捕获变量本身的值了:

// lambda表达式.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。  
//  
  
#include <iostream>  
#include <vector>  
#include <algorithm>  
#include <functional>  
using namespace std;  
  
struct Student  
{  
    string name;  
    float mark;  
      
    Student(string name, float mark)  
    {  
        this->name = name;  
        this->mark = mark;  
    }  
  
    std::function<float(float)> LambdaExpression = [&](float AddMark)mutable->float {return ++mark + AddMark; }; // 改变了捕获变量的值  
};  
  
int main()  
{  
    Student stud1("666", 98.8);  
    float FinalMark = stud1.LambdaExpression(1);  
    cout << stud1.mark << endl; // 99.8 = 98.8 + 1  
}  

 

Exception异常处理  

cpp标准库中有一些类代表异常,这些类都是从exception类派生而来

需要头文件“#include<exception>”:

 

异常

描述

std::exception

该异常是所有标准 C++ 异常的父类。

std::bad_alloc

该异常可以通过 new 抛出。

std::bad_cast

该异常可以通过 dynamic_cast 抛出。

std::bad_exception

这在处理 C++ 程序中无法预期的异常时非常有用。

std::bad_typeid

该异常可以通过 typeid 抛出。

std::logic_error

理论上可以通过读取代码来检测到的异常。

std::domain_error

当使用了一个无效的数学域时,会抛出该异常。

std::invalid_argument

当使用了无效的参数时,会抛出该异常。

std::length_error

当创建了太长的 std::string 时,会抛出该异常。

std::out_of_range

该异常可以通过方法抛出,例如 std::vector std::bitset<>::operator[]()

std::runtime_error

理论上不可以通过读取代码来检测到的异常。

std::overflow_error

当发生数学上溢时,会抛出该异常。

std::range_error

当尝试存储超出范围的值时,会抛出该异常。

std::underflow_error

当发生数学下溢时,会抛出该异常。

 

代码示例

 

可以在lambda的函数体中使用try-catch结构来进行异常处理,并且在其中可以调用exception异常处理库中的内容。如下展示try-catch结构与exception库的联合使用:

// bad_function_call example  
#include <iostream>     // std::cout  
#include <functional>   // std::function, std::plus, std::bad_function_call  
int main () {  
  std::function<int(int,int)> foo = std::plus<int>();  
  std::function<int(int,int)> bar;  
  
  try {  
    std::cout << foo(10,20) << '\n';  
    std::cout << bar(10,20) << '\n';  
  }  
  catch (std::bad_function_call& e)  
  {  
    std::cout << "ERROR: Bad function call\n";  
  }  
  
  return 0;  
}  

 

-> 指定函数返回值类型

->标识符的用处

当你没有指定返回值类型时,lambda表达式会根据return返回的值的类型去自动判断表达式的返回类型。但如果我们用”->”指定了lambda表达式返回的值类型,那么表达式的返回值类型就不会依赖于return返回的数据类型变化而变化。

代码示例

// lambda表达式.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。  
//  
  
#include <iostream>  
#include <vector>  
#include <algorithm>  
#include <functional>  
using namespace std;  
  
struct Student  
{  
    string name;  
    float mark;  
      
    Student(string name, float mark)  
    {  
        this->name = name;  
        this->mark = mark;  
    }  
  
    std::function<float(float)> LambdaExpression = [&](float AddMark)mutable->float {return ++mark + AddMark; }; // 改变了捕获变量的值  
};  
  
int main()  
{  
    Student stud1("666", 98.8);  
    float FinalMark = stud1.LambdaExpression(1);  
    cout << stud1.mark << endl; // 99.8 = 98.8 + 1  
}  
// 可以在lambda函数体内包含像这样的try-catch结构进行异常处理

 

函数体{ }

与不同函数一样,lambda函数也可以有跨多行的函数体,但是通常情况下使用lambda函数是为了避免声明一些短小简单的函数体。不要使用很长(包含多条语句)的lambda表达式,而应转而使用函数对象,因为每次使用lambda表达式时,都需要重新定义它,这无助于提高代码的可重用性。

特别注意:lambda函数返回的函数句柄

函数句柄格式如下

 

格式解析

Function<float(float)>中第一个float是函数返回类型,第二个float是形参传入的类型。其中LambdaExpression是个函数句柄,因此才有如下调用格式:

float FinalMark = stud1.LambdaExpression(1); 

   

lambda函数实现一元谓词

#include <functional>  
#include <iostream>  
#include <algorithm>  
#include <vector>  
using namespace std;  
  
int main()  
{  
    vector<int> VectorArray{ 6,3,1,4,7,5,3,2 };  
    int divisor = 2;  
    vector<int>::iterator itor = find_if(VectorArray.begin(), VectorArray.end(), [&divisor](int Element) {return Element % divisor == 0; });  
    cout << *itor << endl; // 第一个偶数元素为6  
}  

 

Lambda函数实现二元谓词

#include <vector>  
#include <algorithm>  
#include <iostream>  
using namespace std;  
  
int main()  
{  
    vector<int> VectorArray{ 2,1,5,4,9,6,7 };  
    sort(VectorArray.begin(), VectorArray.end(), [](const int& var1, const int& var2)->bool {return var1 > var2; });  
    for_each(VectorArray.begin(), VectorArray.end(), [](const int& var) {cout << var << " "; });  
}  

 

在transform函数中使用Lambda函数

#include <iostream>  
#include <algorithm>  
#include <vector>  
using namespace std;  
  
int main()  
{  
    vector<int> VectorArray1{ 1,10,5,2,8,4 }, VectorArray2{ 6,3,8,3,0,2 }, VectorArray3(VectorArray2.size());  
    // 在把数据装入VectorArray3之前,一定要预留充足的空间,否则会报错  
    transform(VectorArray1.begin(), VectorArray1.end(), VectorArray2.begin(), VectorArray3.begin(), [](const int& var1, const int& var2)->int {return var1 * var2; });  // 将两个动态数组内的元素相乘
    for_each(VectorArray3.begin(), VectorArray3.end(), [](int& var) {cout << var << " "; });  // 输出每个元素
}  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥肥胖胖是太阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值