C++ 函数对象、仿函数与 Lambda 表达式详解
C++ 函数对象、仿函数与 Lambda 表达式详解
在 C++ 中,函数对象(Function Object)、仿函数(Functor) 和 Lambda 表达式 是三种实现可调用行为的技术,它们在功能上类似,但语法和适用场景有所不同。
一、函数对象(仿函数)
函数对象 是通过重载 operator() 的类实例,使对象可以像函数一样被调用。它本质是类对象,但行为类似函数,因此也被称为 仿函数。
1. 基本用法
class Adder {
public:Adder(int n) : num(n) {}int operator()(int x) const {return x + num;}
private:int num;
};int main() {Adder add5(5);cout << add5(3); // 输出 8
}
 
Adder类重载了operator(),实例add5可以像函数一样调用。- 优点:可以保存状态(如成员变量 
num),比函数指针更灵活。 
2. 应用场景
- STL算法:如 
std::sort、std::transform需要传入自定义逻辑时。 - 需要状态的函数:例如计数器、累加器等。
 - 模板元编程:作为策略类传递。
 
二、Lambda 表达式
Lambda 表达式 是 C++11 引入的匿名函数,语法简洁,可捕获上下文变量。编译器会将其转换为匿名函数对象。
1. 基本语法
[捕获列表](参数列表) -> 返回类型 { 函数体 }
 
-  
捕获列表:指定如何捕获外部变量(值捕获
[x]、引用捕获[&x]、隐式捕获[=]或[&])。 -  
mutable:允许修改按值捕获的变量(默认
const)。 -  
示例:
auto add5 = [num=5](int x) { return x + num; }; cout << add5(3); // 输出 8 
2. 底层实现
Lambda 会被编译为一个匿名类,类似仿函数:
// 编译器生成的类
class __AnonymousLambda {
public:__AnonymousLambda(int n) : num(n) {}int operator()(int x) const { return x + num; }
private:int num;
};
 
3. 高级特性
-  
C++14:支持泛型 Lambda(参数用
auto)。auto print = [](auto x) { cout << x; }; -  
C++20:支持模板 Lambda 和
[=, this]显式捕获this。 
三、函数对象 vs Lambda 表达式
| 特性 | 函数对象 | Lambda 表达式 | 
|---|---|---|
| 语法复杂度 | 需要显式定义类 | 内联匿名,语法简洁 | 
| 状态管理 | 通过成员变量保存状态 | 通过捕获列表管理外部变量 | 
| 重载 operator() | 支持多个重载版本 | 单一函数体,不支持重载 | 
| 性能 | 通常可内联,高效 | 同函数对象,编译器优化 | 
| 适用场景 | 复杂逻辑、需要复用的操作 | 简单、一次性使用的逻辑 | 
四、选择建议
- 使用 Lambda:当逻辑简单、临时使用,或需要捕获局部变量时。
 - 使用函数对象:当需要复用逻辑、维护复杂状态,或需要重载 
operator()时。 
五、示例对比
1. 排序比较器
仿函数实现:
struct Compare {bool operator()(int a, int b) const { return a > b; }
};
vector<int> vec {3, 1, 4};
sort(vec.begin(), vec.end(), Compare());
 
Lambda 实现:
sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });
 
2. 带状态的累加器
仿函数实现:
class Counter {int count = 0;
public:int operator()() { return ++count; }
};
Counter cnt;
cout << cnt() << cnt(); // 输出 1 2
 
Lambda 实现:
auto counter = [count=0]() mutable { return ++count; };
cout << counter() << counter(); // 输出 1 2
 
六、总结
- 函数对象/仿函数:适合复杂、可复用的逻辑,或需要重载的场景。
 - Lambda 表达式:适合简单、临时的操作,尤其是需要捕获上下文时。
 - 底层一致性:Lambda 本质是编译器生成的函数对象,二者性能相同。
 
