目录
引入
向算法传递函数:谓词
谓词:是一个可调用的表达式,其返回结果是一个能用作条件的值。STL中的谓词分为:一元谓词和二元谓词。区别是接受一个参数和接受两个参数。如下举例说明:
bool isShorter(const string s1, const string s2){
return s1.size()<s2.size();
}
//排序算法
sort(v.begin(),v.end(),isShorter);//按长度由短至长度排序 v。 isShorter就是一个谓词。
然而,根据算法接受一元谓词还是二元谓词,我们传递给算法的谓词必须严格接受一个或两个参数。但是有时我们希望进行的操作有更多的参数,超出算法对谓词的限制。
例如:给一个vector<string>类型的容器,我们需要找出第一个大于等于给定长度的元素。
我们可以使用 find_if 算法来查找第一个具有特定大小的元素。我们编写一个函数,令其接受一个string和一个长度,并返回一个bool值表示该string的长度是否大于给定长度这很容易,但是 find_if 算法第三个参数是一个一元谓词,它只接受一个参数,所以没有办法传递给它第二个参数表示长度,为了解决这个问题,我们引入lambda表达式。
lambda表达式
介绍lambda:
一个lambda表达式代表一个可调用的代码单元。我们可以将理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型,一个参数列表和一个函数体。但是与函数不同的是,lambda可能定义在函数内部(一般被function封装在函数内部)。它的形式如下:
[capture list] (parameter list) -> return type {function body}
其中,capture list(捕获列表) 是一个 lambda 所在函数中定义的局部变量的列表(通常为空),return type,parameter list,function body 与普通函数一样分别表示:返回类型,形参列表,函数体。但是 lambda 必须使用尾至返回来指定返回类型。 我们可以忽略参数列表和返回类型但是不可以忽略捕获列表和函数体。
eg:auto f = [] {return 42;}。如果lambda的函数体没有返回类型,那么返回void。
向lambda传递参数
和普通函数传参相似,实参和形参必须相对应。
lambda捕获和返回(重中之重)
空捕获列表代表不使用它所在函数中的任何局部变量。
显示捕获
按值捕获 ([x]): 捕获外部变量 x 的副本,lambda 内部对 x 的修改不会影响外部变量 x。
#include <iostream>
int main() {
int x = 10;
auto lambda = [x]() {
std::cout << "Captured by value: " << x << std::endl;
};
lambda(); // 输出: Captured by value: 10
x = 20;
lambda(); // 仍然输出: Captured by value: 10
return 0;
}
按引用捕获 ([&x]): 捕获外部变量 x 的引用,lambda 内部对 x 的修改会影响外部变量 x。
#include <iostream>
int main() {
int x = 10;
auto lambda = [&x]() {
std::cout << "Captured by reference: " << x << std::endl;
x += 10;
};
lambda(); // 输出: Captured by reference: 10
std::cout << "Modified x: " << x << std::endl; // 输出: Modified x: 20
return 0;
}
隐式捕获
捕获所有变量 ([=]): 按值捕获外部作用域中的所有变量。
#include <iostream>
int main() {
int x = 10, y = 20;
auto lambda = [=]() {
std::cout << "Captured by value: x = " << x << ", y = " << y << std::endl;
};
lambda(); // 输出: Captured by value: x = 10, y = 20
return 0;
}
捕获所有变量(按引用) ([&]): 按引用捕获外部作用域中的所有变量。
#include <iostream>
int main() {
int x = 10, y = 20;
auto lambda = [&]() {
std::cout << "Captured by reference: x = " << x << ", y = " << y << std::endl;
x += 10;
y += 20;
};
lambda(); // 输出: Captured by reference: x = 10, y = 20
std::cout << "Modified x = " << x << ", y = " << y << std::endl; // 输出: Modified x = 20, y = 40
return 0;
}
混合捕获:([=, &x]
): 按值捕获所有变量,但按引用捕获指定的变量 x
。类似的,如果前边是按引用捕获所有变量,那么逗号后必须使用值捕获。
#include <iostream>
int main() {
int x = 10, y = 20;
auto lambda = [=, &x]() {
std::cout << "Captured by value: y = " << y << std::endl;
std::cout << "Captured by reference: x = " << x << std::endl;
x += 10; // 修改捕获的 x
};
lambda(); // 输出: Captured by value: y = 20
// 输出: Captured by reference: x = 10
std::cout << "Modified x = " << x << std::endl; // 输出: Modified x = 20
return 0;
}
可变lambda
lambda 表达式默认是不可变的,这意味着一旦定义了 lambda 表达式,它的捕获变量的捕获方式和 lambda 的状态就不能被修改。然而,从 C++14 开始,引入了可变 lambda 表达式(mutable lambda),允许你在 lambda 表达式内部修改捕获的按值变量。这通过使用 mutable 关键字来实现。示例如下:
若不加mutable是不可以修改变量的:
如下是加mutable后的代码:
#include <iostream>
int main() {
int x = 10;
// 定义一个可变 lambda
auto lambda = [x]() mutable {
std::cout << "Before modification: " << x << std::endl;
x += 10; // 修改捕获的 x
std::cout << "After modification: " << x << std::endl;
};
lambda(); // 输出: Before modification: 10
// 输出: After modification: 20
std::cout << "Outside lambda: " << x << std::endl; // 输出: Outside lambda: 10
return 0;
}
指定lambda返回类型
不需要输入返回类型
1.单一返回值:如果lambda表达式只有一个返回值,编译器可以推断出返回类型。例如:
auto lambda = [](int x) { return x + 1; };
2.不返回值(void):如果lambda表达式没有返回值,编译器会推断返回类型为void
。例如:
auto lambda = [] { std::cout << "Hello, world!"; };
3.类型推断明确:如果编译器能够从返回表达式中清晰地推断出返回类型(只有一个return语句),即使lambda有多个返回路径,编译器也能够正确推断。例如:
auto lambda = [](bool condition) { return condition ? 1 : 0; };
需要输入返回类型
当lambda表达式的返回类型比较复杂或难以推断时,你可能需要显式地指定返回类型。例如,涉及到模板或多重返回类型时,编译器可能无法正确推断出返回类型:
auto lambda = [](double x) -> decltype(x * x) { return x * x; };
// 使用 decltype 显式指定返回类型
多个不同的返回类型:如果lambda表达式有多个不同的返回路径且它们的类型不同(return的类型不同),编译器可能无法决定一个统一的返回类型。在这种情况下,需要显式地指定返回类型:
auto lambda = [](bool condition) ->int {
if (condition) {
return 1;
}
else {
// 下面的代码会导致返回类型无法确定,因为 return 2.3是double类型和1不同
return 2.3;
}
};
lambda的实际应用
最后我们回到引入的那个问题,如何给一元或者二元谓词传入更多的参数使算法成立,经过以上的学习,应该对lambda表达式有一定的了解,写出来应该不难,如下所示:
#include <bits/stdc++.h>
using namespace std;
#include <cctype>
int main()
{
vector<string> v{ "hello","world!" };
int sz = 6;
auto wc = find_if(v.begin(), v.end(), [sz](const string& a) {return a.size() >= sz; });
cout << *wc << endl;
}
lambda表达式还有很多用法,我目前用的比较多的是用function调用lambda表达式,调用模板为function<return type(参数1,参数2..参数n)> 表达式名称 = lambda表达式。在写递归的时候十分推荐这种写法,例如:在使用深度优先搜索算法的时候等等。
#include <iostream>
#include <functional>
int main() {
// 定义 std::function 使用 lambda 表达式
std::function<int(int, int)> add = [](int a, int b) { return a + b; };
// 调用 std::function
std::cout << "Sum: " << add(3, 4) << std::endl;
return 0;
}
当然也可以用如下的方法调用lambda表达式
#include <iostream>
#include <functional>
using namespace std;
int main()
{
int a = 10, b = 20;
auto add = [&]() {return a + b; };
cout << add(); //30
}
总结
在实际应用中所讲述的两种方式,我更推荐第一种用function来调用lambda表达式,我个人认为他比较美观(其实感觉比较diao,可以zhuangbei)。谢谢观看,注意改颜色的知识请着重观看,觉得博主写的不错的请点个赞+收藏+关注吧,后面还会写一些比较实用的知识。如有不足,欢迎评论区点评!