1.lambda表达式
Lambda 表达式是 C++11 引入的一个非常强大的特性,它允许你在代码中定义匿名函数(没有名字的函数对象),常用于需要传递函数作为参数的地方,比如 STL 算法中的回调。
1.1基本语法
[capture](parameters) -> return_type {
// 函数体
}
部分 | 描述 |
---|---|
[capture] |
捕获列表:指定lambda可以访问哪些外部变量(局部变量、this指针等) |
(parameters) |
参数列表,和普通函数一样 |
-> return_type |
返回类型(可省略,编译器自动推导) |
{ ... } |
函数体 |
1.2捕获列表
捕获列表用于告诉编译器 lambda 表达式可以访问哪些外部变量。
写法 | 含义 |
---|---|
[] |
不捕获任何外部变量 |
[=] |
按值捕获所有外部变量(只读) |
[&] |
按引用捕获所有外部变量(可修改) |
[x] |
按值捕获变量 x |
[&x] |
按引用捕获变量 x |
[x, &y] |
按值捕获 x,按引用捕获 y |
[this] |
捕获当前类的 this 指针(在类内部使用) |
[=, &x] |
按值捕获所有变量,但 x 按引用捕获 |
[&, x] |
按引用捕获所有变量,但 x 按值捕获 |
1.3参数列表(parameters)
这部分和普通函数的参数列表一样。如果不需要参数,可以省略括号或者写成 ()
。
[](int x, int y) { cout << x + y; }
1.4.返回类型 -> return_type
可以省略,如果省略的话,编译器会根据 return 语句自动推导返回类型。如果函数体没有 return,则返回 void。
[](int a, int b) -> int {
return a + b;
}
1.5.使用示例
示例 1:最简单的 lambda 表达式
#include <iostream>
using namespace std;
int main() {
auto func = []() {
cout << "Hello from lambda!" << endl;
};
func(); // 调用 lambda
return 0;
}
示例 2:带参数和返回值的 lambda
#include <iostream>
using namespace std;
int main() {
auto sum = [](int a, int b) -> int {
return a + b;
};
cout << "Sum: " << sum(3, 5) << endl; // 输出 8
return 0;
}
示例 3:捕获变量
#include <iostream>
using namespace std;
int main() {
int x = 10;
// 按值捕获 x
auto f1 = [x]() { cout << "x = " << x << endl; };
x = 20;
f1(); // 输出 10(因为是按值捕获)
// 按引用捕获 x
auto f2 = [&x]() { cout << "x = " << x << endl; };
x = 30;
f2(); // 输出 30(因为是按引用捕获)
return 0;
}
示例 4:在 STL 算法中使用 lambda
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5};
// 使用 for_each 遍历并打印
for_each(v.begin(), v.end(), [](int n) {
cout << n << " ";
});
cout << endl;
// 使用 sort 排序,降序
sort(v.begin(), v.end(), [](int a, int b) {
return a > b;
});
return 0;
}
1.6注意事项
- Lambda 表达式本质上是一个函数对象(functor),由编译器自动生成。
- 如果你想要将 lambda 存储为函数指针,必须确保它不捕获任何变量(即捕获列表为空)。
- 可以将 lambda 作为参数传递给其他函数,如线程、事件处理等。
- 在类成员函数中使用 lambda 时,注意是否要捕获
this
来访问成员变量或方法。
1.7高级用法 mutable 和状态保持
默认情况下,lambda 的 operator() 是 const 的,不能修改按值捕获的变量。使用 mutable
可以解除这个限制。
int x = 10;
auto f = [x]() mutable {
x += 5;
cout << x << endl;
};
f(); // 输出 15
cout << x << endl; // 还是 10(因为是按值捕获)
Lambda 表达式让 C++ 编程更加灵活简洁,尤其适用于算法、异步任务、事件驱动等场景。
2.包装器function
在 C++ 中,**函数包装器(Function Wrapper)**是一种能够封装各种可调用对象(如普通函数、lambda 表达式、函数对象、成员函数等)的通用机制。它们提供统一的接口来调用这些不同的可调用对象。
C++ 中主要的函数包装器有:
包装器类型 | 说明 |
---|---|
std::function |
通用函数包装器,支持任意符合签名的可调用对象 |
std::bind |
将参数绑定到函数上,生成新的可调用对象 |
Lambda 表达式 | 匿名函数对象,本质上是函数对象的一种 |
函数指针 | 原始方式,但功能有限 |
2.1.function
定义
#include <functional>
std::function<返回类型(参数类型...)> f;
它是一个模板类,用于封装任何可以调用的对象(函数、lambda、functor、绑定表达式等),只要它们的调用形式匹配。
示例:
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
void hello() {
std::cout << "Hello from function!" << std::endl;
}
int add(int a, int b) {
return a + b;
}
int main() {
// 1. 包装普通函数
std::function<void()> f1 = hello;
f1(); // 输出: Hello from function!
// 2. 包装 lambda 表达式
std::function<int(int, int)> f2 = [](int a, int b) { return a * b; };
std::cout << "Multiply: " << f2(3, 4) << std::endl; // 输出: 12
// 3. 包装函数指针
std::function<int(int, int)> f3 = add;
std::cout << "Add: " << f3(5, 6) << std::endl; // 输出: 11
// 4. 在 STL 算法中使用
std::vector<int> v = {1, 2, 3, 4, 5};
std::function<void(int)> printFunc = [](int n) {
std::cout << n << " ";
};
std::for_each(v.begin(), v.end(), printFunc); // 输出: 1 2 3 4 5
return 0;
}
std::function
可以为空,使用前最好检查是否有效:
if (f1) f1();
-
如果赋值不匹配调用签名,编译器会报错。
-
内部使用了 **类型擦除(type erasure)**技术,有一定的性能开销(但通常可以忽略)。
2.2 bind
定义:std::bind
用来将参数绑定到一个函数或可调用对象上,生成一个新的可调用对象。
示例:
#include <iostream>
#include <functional>
using namespace std::placeholders;
int multiply(int a, int b) {
return a * b;
}
int main() {
// 绑定第一个参数为 10
auto func1 = std::bind(multiply, 10, _1);
std::cout << func1(5) << std::endl; // 输出 50 → multiply(10, 5)
// 绑定第二个参数为 5
auto func2 = std::bind(multiply, _1, 5);
std::cout << func2(7) << std::endl; // 输出 35 → multiply(7, 5)
// 绑定两个参数
auto func3 = std::bind(multiply, 2, 3);
std::cout << func3() << std::endl; // 输出 6
return 0;
}
占位符 _1
, _2
...
_1
表示第一个参数_2
表示第二个参数- ...依此类推
这些占位符定义在 <functional>
中的命名空间 std::placeholders
。
结合 std::function
使用
std::function<int(int)> f = std::bind(multiply, 2, _1);
std::cout << f(5) << std::endl; // 输出 10
2.3实际应用场景
场景 1:事件系统 / 回调机制
class Button {
public:
using Callback = std::function<void()>;
void setOnClick(Callback cb) {
onClick = cb;
}
void click() {
if (onClick) onClick();
}
private:
Callback onClick;
};
// 使用
Button btn;
btn.setOnClick([]() {
std::cout << "Button clicked!" << std::endl;
});
btn.click(); // 输出: Button clicked!
场景 2:策略模式 / 算法选择
enum class Operation { Add, Multiply };
std::function<int(int, int)> getCalculator(Operation op) {
if (op == Operation::Add)
return [](int a, int b) { return a + b; };
else
return [](int a, int b) { return a * b; };
}
auto calc = getCalculator(Operation::Multiply);
std::cout << calc(3, 4) << std::endl; // 输出 12
类型 | 优点 | 缺点 |
---|---|---|
std::function |
通用性强,适配多种可调用对象 | 性能略低,可能堆分配 |
std::bind |
参数绑定灵活,适合部分应用 | 语法复杂,调试困难 |
Lambda | 简洁高效,代码内联 | 不易复用,作用域问题 |
函数指针 | 最快,兼容 C | 功能有限,无法捕获变量 |