上一篇讲道的函数对象,虽然可以传递状态,但是得创建一个对象并重写operator方法,略显麻烦
Lambda就是来解决这个痛点的。Lambda 表达式就是一个能直接写在代码里、当场定义的小函数,不用先起名字、不用单独定义。它的形式是:
[捕获列表](参数列表) mutable(可选) noexcept(可选) -> 返回类型(可选) {
函数体
}
瞬间懵逼,这都是些什么玩意呀
别被这一长串吓到。实际开发中,大部分时候你只需要前三个部分:捕获列表、参数列表、函数体。后面的都是锦上添花。
咱们拆开来看:
- 捕获列表:决定Lambda能访问外部哪些变量。这是Lambda最核心的设计。
- 参数列表:和普通函数一样,传参用。
- mutable:默认情况下,Lambda的operator()是const的。加上mutable,你就可以修改捕获的副本了。
- noexcept:声明这个Lambda不会抛异常。
- 返回类型:大部分情况下编译器能自动推导,不用写。
直接看一段简单的代码吧
cpp
#include <iostream>
#include <functional>
int main() {
int threshold = 10;
std::function<bool(int)> isBig = [threshold](int x) mutable noexcept -> bool {return x > threshold;};
std::cout << isBig(15) << '\n'; // 输出 1
return 0;
}
觉得std::function<bool(int)>很长的话可以用auto代替
像这样
cpp
#include <iostream>
int main() {
int threshold = 10;
auto isBig = [threshold](int x) mutable noexcept -> bool {return x > threshold;};
std::cout << isBig(15) << '\n'; // 输出 1(true)
return 0;
}
运行结果
cpp
1
各项含义:
-
[threshold]:捕获列表,按值捕获threshold(闭包内持有一份副本)。 -
(int x):参数列表。 -
mutable:允许在函数体内修改按值捕获的副本(本例没修改,所以可省)。 -
noexcept:声明该 lambda 不抛异常(本例可省)。 -
-> bool:显式指定返回类型(本例可省,编译器自动推导为bool)。 -
{ return x > threshold; }:函数体。
可以简化成下面这样,结果是一样的
cpp
#include <iostream>
int main() {
int threshold = 10;
auto isBig = [threshold](int x) { return x > threshold; }; // 捕获 threshold
std::cout << isBig(15) << '\n'; // 输出 1(true)
return 0;
}
常见用法:直接当参数传
cpp
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5};
std::sort(v.begin(), v.end(), [](int a, int b) { // 当场写一个比较函数传进去
return a > b; // 降序
});
for (int x : v) std::cout << x << ' '; // 输出 5 4 3 1 1
std::cout << '\n';
return 0;
}
运行结果
cpp
5 4 3 1 1
这里有个小疑问,std::sort的第三个参数是什么?
它的函数原型是这样的
cpp
void sort(RandomIt first, RandomIt last, Compare comp);
Compare的函数签名大概是这样
bool(const T&, const T&)
标准对 Compare 的要求不是"必须精确写成 bool(const T&, const T&)",而是:
对于解引用迭代器得到的类型
T,comp必须能用两个T类型的实参调用 ,且返回值可转换为bool。
也就是说,下面这些都能作为 comp:
cpp
bool f1(int a, int b); // 按值
bool f2(const int& a, const int& b); // 按 const 引用
struct C { bool operator()(int a, int b) const; };
auto l = [](int a, int b){ return a < b; };
自己实现一个接收lamda表达式的方法
把可调用物的类型做成模板参数,这样能接受函数指针、函数对象、lambda 等任何可调用物,且零开销(编译期确定,无类型擦除):
cpp
#include <iostream>
#include <vector>
// Func 是任意可调用物,action 是它的一个实例
template<typename Func>
void myforEach(const std::vector<int>& v, Func action) {
for (int x : v) {
action(x); // 调用传进来的可调用物
}
}
int main() {
std::vector<int> v = {1, 2, 3};
// 传函数对象
struct Print { void operator()(int x) const { std::cout << x << ' '; } };
myforEach(v, Print{});
std::cout << '\n';
// 传 lambda
int sum = 0;
myforEach(v, [&sum](int x){ sum += x; });
std::cout << "sum = " << sum << '\n'; // sum = 6
return 0;
}
运行结果
cpp
1 2 3
sum = 6
关于lamda还有一些知识点,就留在下一篇继续讲解吧