lambda表达式
lambda表达式一般用于定义匿名函数,使得代码更加灵活简洁。lambda表达式与普通函数类似,也有参数列表、返回值类型和函数体,只是它的定义方式更简洁,并且可以在函数内部定义。
语法:[captures](params) ->trailing-return-type{body}
captures 捕获列表,lambda可以把上下文变量以值或引用的方式捕获,在body中直接使用
params 是传入的参数列表
->trailing-return-type表示返回值类型,可省略,但要写出时->和trailing-return-type必须同时出现
body 函数体,函数的具体逻辑
本文中程序可通过Visual Studio直接运行验证
1参数列表的使用
#include<iostream>
using namespace std;
int main() {
auto plus = [](int v1, int v2) -> int { return v1 + v2; };
int sum = plus(1, 2);
cout << sum << endl;
}
#include<vector>
#include <algorithm>
#include<iostream>
using namespace std;
struct Item
{
Item(int aa, int bb) : a(aa), b(bb) {}
int a;
int b;
};
int main()
{
std::vector<Item> vec;
vec.push_back(Item(1, 19));
vec.push_back(Item(10, 3));
vec.push_back(Item(3, 7));
vec.push_back(Item(8, 12));
vec.push_back(Item(2, 1));
//打印vec中的item成员
std::for_each(vec.begin(), vec.end(),
[](const Item& item) { std::cout << item.a << " " << item.b << std::endl; });
return 0;
}
2 捕获列表的使用
lambda表达式的可捕获的量的是在其定义点周围可见的局部变量和静态变量,以及非静态成员函数的this指针
[=] 按值(复制)的方式捕获所有可捕获的量
[&] 按引用的方式捕获所有可捕获的量
[a]仅按值方式捕获a
[=, &a]按值的方式捕获其他变量所有可捕获的量, 按引用方式捕获量a。这里可以按引用捕获多个,例如 [=, &a, &b,&c]。这里注意,如果前面加了=,后面加的具体的参数必须以引用的方式来捕获,否则会报错。
[&, a] 按引用方式捕获其他变量所有可捕获的量, 按值方式捕获量a。这里后面的参数也可以多个,例如 [&, a, b, c]。这里注意,如果前面加了&,后面加的具体的参数必须以值的方式来捕获。
[this]仅捕获this指针。捕获this指针的lambda表达式出现在非静态成员函数中,每个非静态成员函数都包含this指针,指向调用该函数的对象。对于类的非静态成员变量和非静态成员函数而言,它们是属于类对象实例的一部分,lambda 表达式并不绑定到某个对象实例,因此不在类对象的作用域内(如果在 lambda 表达式中直接访问非静态成员变量,那么编译器将无法确定这些变量属于哪个对象实例),只有通过捕获this指针,才能访问和修改非静态成员变量,才能调用非静态成员函数。捕获this指针适用于需要访问当前对象状态的场景,通过this指针在lambda表达式中所做的修改会反映到原始对象上。
#include <iostream>
int main()
{
int a = 3;
int b = 5;
//按值来捕获
auto func1 = [a] { std::cout << a << std::endl; };
func1();
//按值来捕获
auto func2 = [=] { std::cout << a << " " << b << std::endl; };
func2();
//按引用来捕获
auto func3 = [&a] {
a = a + 1;
std::cout << a << std::endl; };
std::cout << a << std::endl;
func3();
//按引用来捕获
auto func4 = [&] { std::cout << a << " " << b << std::endl; };
func4();
}
#include<iostream>
class MyClass {
public:
//每个非静态成员函数都包含this指针,指向调用该函数的对象
void someMethod() {
//lambda表达式外,可以正常访问和修改类的非静态成员变量
myMember += 2;
//lambda表达式外,可以正常访问非静态成员函数
add(2);
//创建一个lambda表达式,捕获指向当前对象的指针
auto lambda = [=](int value) {
myMember += value;//lambda表达式内,捕获this指针后,才能访问和修改非静态成员变量,也可使用this->myMember += value;
add(2);//lambda表达式内,捕获this指针后,才能调用非静态成员函数,也可使用this->add(2);
};
//调用lambda表达式
lambda(10);
}
void add(int a)
{
myMember += a;
}
int myMember = 2;
};
int main() {
MyClass obj;
obj.someMethod();
//输出修改后的 myMember 值
std::cout << "Modified myMember: " << obj.myMember << std::endl;
return 0;
}
3 lambda表达式捕获函数
lambda表达式可以捕获外部作用域中的变量,对于函数来说,由于它们不是在 lambda表达式的局部作用域内定义的,因此它们不需要被捕获就可以在lambda 表达式中直接使用。在程序中不能直接捕获函数。然而,可以通过捕获函数指针或者函数对象来间接地捕获函数。捕获函数指针或函数对象时,lambda 表达式会创建对这些对象的副本,因此如果对象较大或者复制成本较高,可能会影响性能。在这种情况下,可以考虑捕获引用或者使用其他方式来避免不必要的复制。
#include <iostream>
#include <functional> //用于std::function
//普通函数
int add(int a, int b) {
return a + b;
}
int main() {
//函数指针
int (*funcPtr)(int, int) = add;
//捕获函数指针
auto lambdaWithFuncPtr1 = [funcPtr](int x, int y) {//[&funcPtr]
return funcPtr(x, y);
};
std::cout << "Result from lambda with function pointer: " << lambdaWithFuncPtr1(3, 4) << std::endl;
//函数对象,
std::function<int(int, int)> funcObj = add; //funcObj是std::function类模板(也称为包装器)的实例
//捕获函数对象
auto lambdaWithFuncObj = [funcObj](int x, int y) {
return funcObj(x, y);
};
std::cout << "Result from lambda with function object: " << lambdaWithFuncObj(3, 4) << std::endl;
return 0;
}
#include <iostream>
#include <vector>
#include <numeric>//为使用std::accumulate
template<typename V, typename M, typename F>
void func(V& vec, M& mean, F&& fun) {
size_t len = vec.size();
mean = std::accumulate(vec.begin(), vec.end(), M(0),
[&fun](const M& sum, const auto& data)->M {return sum + fun(data); }) / len;//此时fun是函数对象,也即F类型的实例(F类型表示整个lambda表达式的类型),必须通过捕获才能被lambda表达式使用
}
int main() {
std::vector<int> numbers = { 1, 2, 3, 4, 5 };
int mean;
func(numbers, mean, [](const int& x)->int {
return x;
});
std::cout << mean << std::endl;
return 0;
}
4 lambda表达式的本质
编译器会把我们写的lambda表达式翻译成一个类,并重载operator()来实现
①不捕获变量,仅传参
#include<iostream>
using namespace std;
class LambdaClass
{
public:
int operator () (int a, int b) const
{
return a + b;
}
};
int main() {
auto plus1 = [](int v1, int v2) -> int { return v1 + v2; };
int sum1 = plus1(1, 2);
cout << sum1 << endl;
LambdaClass plus2;
int sum2 = plus2(1, 2);
cout << sum2 << endl;
}
②按值方式捕获变量,且传参
值捕获时,编译器会把捕获到的值作为类的成员变量,并且变量是以值的方式传递的。需要注意的时,如果所有的参数都是值捕获的方式,那么生成的operator()函数是const函数的,是无法修改捕获的值的,哪怕这个修改不会改变lambda表达式外部的变量,如果想要在函数内修改捕获的值,需要加上关键字 mutable。
#include<iostream>
using namespace std;
class LambdaClass
{
public:
LambdaClass(int xx, int yy)
: x(xx), y(yy) {}
int operator () (int a, int b) const
{
return x + y + a + b;
}
private:
int x;
int y;
};
int main() {
int x = 1; int y = 2;
auto plus1 = [=](int a, int b) -> int { return x + y + a + b; };
int c1 = plus1(1, 2);
cout << c1 << endl;
LambdaClass plus2(x, y);
int c2 = plus2(1, 2);
cout << c2 << endl;
}
#include<iostream>
using namespace std;
class LambdaClass
{
public:
LambdaClass(int xx, int yy)
: x(xx), y(yy) {}
int operator () (int a, int b) const
{
x++;
y++;
return x + y + a + b;
}
private:
mutable int x;
mutable int y;
};
int main() {
int x = 1; int y = 2;
auto plus1 = [=](int a, int b) mutable -> int { x++; y++; return x + y + a + b; };
int c1 = plus1(1, 2);
cout << c1 << endl;
LambdaClass plus2(x, y);
int c2 = plus2(1, 2);
cout << c2 << endl;
}
③按引用方式捕获变量,且传参
以引用的方式捕获变量,和值捕获的方式有3个不同的地方:
1.参数引用的方式进行传递;
2.引用捕获在函数体修改变量,会直接修改lambda表达式外部的变量
3.opeartor()函数不是const的
#include<iostream>
using namespace std;
class LambdaClass
{
public:
LambdaClass(int& xx, int& yy)
: x(xx), y(yy) {}
int operator () (int a, int b)
{
x++;
return x + y + a + b;
}
private:
int& x;
int& y;
};
int main() {
int x = 1; int y = 2;
cout << "x=" << x << endl;
auto plus1 = [&](int a, int b) -> int { x++; return x + y + a + b; };
int c1 = plus1(1, 2);
cout << c1 << endl;
cout << "x=" << x << endl;
LambdaClass plus2(x, y);
int c2 = plus2(1, 2);
cout << c2 << endl;
cout << "x=" << x << endl;
}
🐂🐂