lambda函数
lambda表达式介绍
lambda表达式: C++11引入的一项新技术,表示一个可调用的代码单元。可理解为一个未命名的内联函数,表示形式:
[capture](parameters) mutable -> return type { fuction body};
#include <iostream>
int main()
{
int iX = 3, iY = 4;
auto f = [](int x, int y)->int{return x+y;}
std::cout<<"f: " << f(iX,iY) << std::endl;
return 0;
}
- lambda函数和普通函数相比,不需要定义函数名,用[]说明是lambda函数;采样追踪返回类型的方式声明其返回值。其他方面和普通函数一样;
- 直观的讲,lambda函数和普通函数最大的区别之一是:lambda函数可以通过捕获列表访问一些上下文中的数据(即捕获列表指定了上下文中的哪些数据可以被lambda使用);
- lambda函数等同于一个“局部函数(即在函数作用域中定义的函数)”。局部函数通常仅属于其父作用域,能访问父作用域的变量,且在其父作用域中使用。C/C++语言标准不允许局部函数的存在,但C++11标准使用lambda函数打破了这个规则;
捕获列表
捕获列表[capture]: []是lambda表达式开始的标志,是指定lambda可以使用局部变量的地方,由如下形式:
表示 | 说明 |
---|---|
[ ] | 默认不捕获任何变量,但函数体内可以使用静态变量和全局变量 |
[name] | 值捕获,可以有多个,逗号分割(只捕获捕获列表中的参数,这些参数在lambda所在的函数中是已存在的),如 [iCount,szName] |
[&name] | 引用捕获,可以有多个,逗号分割(只捕获捕获列表中的参数),lambda函数中对捕获的参数改变,外面的参数值也随之改变 |
[=] | lambda所在的函数中所有已存在的变量都被依照传值的方式捕获 |
[&] | lambda所在的函数中所有已存在的变量都被依照传引用的方式捕获 |
[=,&name] | 值捕获lambda所在的函数中所有已存在的变量,除了引用捕获的变量,引用捕获的变量按照引用捕获处理 |
[&,name] | 引用捕获lambda所在的函数中所有已存在的变量,除了值捕获的变量,值捕获的变量按照值捕获处理 |
[this] | 值捕获,捕捉当前的this指针,让lambda表达式有和当前类成员函数同样的访问权限 |
全局变量和静态变量
#include <iostream>
int iParam = 10; //全局变量
int main()
{
//static int iParam = 10; //静态
auto f = [] {iParam = 20; }; //空捕获
f(); //调用
std::cout << "iParam: " << iParam << std::endl; //iParam: 20
auto f1 = [=]() {iParam = 40; };
f1();
std::cout << "iParam: " << iParam << std::endl; //iParam: 40
auto f2 = [&]() {iParam = 50; };
f2();
std::cout << "iParam: " << iParam << std::endl; //iParam: 50
return 0;
}
值传递
#include <iostream>
int main()
{
int iParam = 10;
auto f = [iParam]() {int iVar = iParam + 2; return iVar; };
//传入的是值,函数体内直接对iParam的复制体操作,对iParam 没影响,f()使用的iParam是此函数上面最新的iParam的值
//后面f()使用的iParam 一直是10,因为每次调用f()都是传的是iParam = 10的复制体
std::cout << "iParam: " << iParam << ", f: " << f() << std::endl; //iParam: 10, f: 12
iParam = 20;
std::cout << "iParam: " << iParam << ", f: " << f() << std::endl; //iParam: 20, f: 12
//
int iParam1 = 10, iParam2 = 10;
//auto f = [iParam1] {iParam1 = 1; }; //不能编译,值传递,iParam1是不能更新的左值,相当于只读,只能使用不能更改
//[name1,name2] 显示捕获,指定要捕获的多个局部变量,用逗号隔开
auto f1 = [iParam1, iParam2] {return iParam1 + iParam2; };
std::cout << "f1: " << f1() << std::endl; //f1: 20
//[=]隐形值捕获,捕获lambda所在的函数中所有已存在的变量
auto f2 = [=] {return iParam1 + iParam2; }; //捕获main()函数中lambda表达式上面的所有局部变量
std::cout << "f2: " << f2() << std::endl; //f2: 20
return 0;
}
引用传递
#include <iostream>
int main()
{
int iParam = 10;
auto f = [&iParam]() {int iVar = iParam + 2; return iVar; };
//传入的是引用,函数体内直接对iParam操作,f()使用的iParam是最新的iParam的值
//后面f()使用的iParam 一直最新的iParam的值,因为每次调用f()都是传的是iParam的最新值
std::cout << "iParam: " << iParam << ", f: " << f() << std::endl; //iParam: 10, f: 12
iParam = 20;
std::cout << "iParam: " << iParam << ", f: " << f() << std::endl; //iParam: 20, f: 22
//
int iParam1 = 10, iParam2 = 10;
//[&name1,&name2] 显示捕获
auto f1 = [&iParam1, &iParam2] {iParam1 = 30, iParam2 = 30; };
f1();
std::cout << "iParam1: " << iParam1 << ", iParam2: " << iParam2 << std::endl; //iParam1: 30, iParam2: 30
//[&]隐形捕获,捕获lambda所在的函数中所有已存在的变量
auto f2 = [&] {iParam1 = 40, iParam2 = 40; };
//捕获main()函数中lambda表达式上面的所有局部变量
f2();
std::cout << "iParam1: " << iParam1 << ", iParam2: " << iParam2 << std::endl; //iParam1: 40, iParam2: 40
return 0;
}
部分引用传递,部分值传递
#include <iostream>
int main()
{
int iParam1 = 10, iParam2 = 10;
//[&,name] 引用捕获lambda所在的函数中所有已存在的变量,除了值捕获的变量,值捕获的变量按照值捕获处理
auto f = [&, iParam1] {iParam2 = 20; return iParam1; };
f();
std::cout << "iParam1: " << iParam1 << ", iParam2: " << iParam2 << std::endl; //iParam1: 10, iParam2: 20
int iParam3 = 10, iParam4 = 10;
//[=,&name] 值捕获lambda所在的函数中所有已存在的变量,除了引用捕获的变量,引用捕获的变量按照值引用捕获处理
auto f1 = [=, &iParam3] {iParam3 = 20; return iParam4; };
f1();
std::cout << "iParam3: " << iParam3 << ", iParam4: " << iParam4 << std::endl; //iParam3: 20, iParam4: 10
return 0;
}
- 只有在捕获列表中的局部变量,才能在lambda函数体中使用;
- lambda函数体中可以直接使用静态变量和全局变量;
- 尽量避免捕获指针和引用;
C++ lambda表达式及其原理
C++ lambda表达式
C++11—lambda函数
参数列表
- 参数列表(parameters):和普通函数参数列表一致。
若不需要输入参数可省略括号"()";
不支持可变参数、不能有默认参数(实参和形参个数永远相等);
#include <iostream>
#include <string>
int main()
{
auto f = [](int iParam, const std::string& szParam)
{
std::cout << "iParam: " << iParam << ", szParam: " << szParam << std::endl;// iParam: 10, szParam: UT
return ++iParam;
};
std::cout << "f: " << f(10, "UT") << std::endl; // f: 11
return 0;
}
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vec{ 1, -4, 2, -3, 5 };
//把容器中值进行排序
std::sort(vec.begin(), vec.end(), [](int a, int b) {return a<b; });
//遍历容器中值
for_each(vec.begin(), vec.end(), [](int a) {std::cout<<"a: "<<a<<std::endl; });
return 0;
}
修饰符
- 修饰符mutable: 默认情况下,lambda函数总是一个const类型函数。mutable修饰符可取消其常量性质的。若使用mutable修饰符,参数列表不可省略(即使参数为空);
- 一般使用默认的即可,很少用到要取消其常量性质;
auto f = []mutable {return 10; }; //无法编译
auto f1 = []()mutable {return 10; }; //正常使用
#include <iostream>
int main()
{
int iParam = 10;
//auto f = [iParam]{iParam += 10; }; //无法编译
auto f = [iParam]()mutable {iParam += 10; std::cout << "iParam: " << iParam << std::endl; };
f(); //iParam = 10+10 = 20
std::cout << "iParam: " << iParam << std::endl; //iParam: 10
f(); //iParam = 10+ 10 +10 = 30
std::cout << "iParam: " << iParam << std::endl; //iParam: 10
}
值传入的参数,是不会被lambda表达式改变的,但在lambda表达式内部,传入的值就像是全局变量。
函数的返回值类型
- 返回值类型->return type: 追踪返回类型形式声明函数的返回类型。
若不需要返回值,可省略;
类型明确时也可省略,编译器会推断返回类型;
编译器不能推断出其返回类型时需要显示指定返回类型;
auto f = []() mutable ->int {return 10; }; //正确
auto f = []() ->int {return 10; };//正确
auto f = []{return 10; };//正确
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vec{ 1, -4, 2, -3, 5 };
//把容器中的负值变为正值
std::transform(vec.begin(), vec.end(), vec.begin(), [](int i) {return i < 0 ? -i : i; }); //VS2010和VS2019都可编译
std::transform(vec.begin(), vec.end(), vec.begin(), [](int i) {if (i < 0) return -i; else return i; });
//VS2010编译失败(无法推测返回值),VS2019可以编译(可推测返回值)
std::transform(vec.begin(), vec.end(), vec.begin(), [](int i) ->int {if (i < 0) return -i; else return i; });//VS2010和VS2019都可编译
return 0;
}
函数体
- 函数体{ fuction body}: 和普通函数的函数体一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。
lambda表达式C++14开始允许形参使用auto
VS2019默认使用C++14
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vec{ 1, -4, 2, -3, 5 };
//把容器中值进行排序
std::sort(vec.begin(), vec.end(), [](auto a, auto b) {return a < b; });
//遍历容器中值
for_each(vec.begin(), vec.end(), [](auto a) {std::cout << "a: " << a << std::endl; });
return 0;
}
VS2010编译以上程序会失败,VS2019可以使用
仿函数实现排序算法调用
#include "Test.h"
#include <vector>
#include <functional>
#include <algorithm>
void ValueSort(std::function<bool(int,int)> func, std::vector<int>& vec)
{
std::sort(vec.begin(), vec.end(), func);
return;
}
bool GreaterThan(int iA, int iB) { return iA >= iB; };
bool LessThan(int iA, int iB) { return iA <= iB; };
int main()
{
std::vector<int> vec{ 1, -4, 2, -3, 5 };
ValueSort(GreaterThan, vec);
for_each(vec.begin(), vec.end(), [](auto a) {std::cout << a << ", " ; }); //5, 2, 1, -3, -4,
std::cout << "" << std::endl;
ValueSort(LessThan, vec);
for_each(vec.begin(), vec.end(), [](auto a) {std::cout << a << ", "; }); //-4, -3, 1, 2, 5,
}
模板函数实现排序算法调用
#include "Test.h"
#include <vector>
#include <functional>
#include <algorithm>
template<class Func>
void ValueSort(Func func, std::vector<int>& vec)
{
std::sort(vec.begin(), vec.end(), func);
return;
}
int main()
{
std::vector<int> vec{ 1, -4, 2, -3, 5 };
ValueSort([](int iA, int iB) { return iA <= iB; }, vec);
for_each(vec.begin(), vec.end(), [](auto a) {std::cout << a << ", "; }); //-4, -3, 1, 2, 5,
}