目录
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 << " "; }); // 输出每个元素
}