Lambda 表达式的基本格式:
[capture-list] (parameters)-> return-type { function-body }各组成部分说明:
下面我们通过代码来看看lambda是怎么用的
int main()
{
// 一个简单的lambda表达式
auto add1 = [](int x, int y)->int {return x + y; };
cout << add1(1, 2) << endl;
// 1、捕捉为空也不能省略
// 2、参数为空可以省略
// 3、返回值可以省略,可以通过返回对象自动推导
// 4、函数体不能省略
auto func1 = []
{
cout << "hello bit" << endl;
return 0;
};
func1();
int a = 0, b = 1;
auto swap1 = [](int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
};
swap1(a, b);
cout << a << ":" << b << endl;
return 0;
}运行结果:

• lambda 表达式默认只能访问函数体和参数中的变量,如需使用外层作用域的变量,需通过捕获列表实现
• 显式捕获方式分为值捕获和引用捕获,多个捕获变量用逗号分隔。例如:[x, y, &z] 表示对x和y进行值捕获,对z进行引用捕获
• 隐式捕获方式通过在捕获列表中使用符号自动捕获变量:
• 混合捕获方式允许同时使用显式和隐式捕获:
• 局部作用域中的lambda表达式:
• 捕获变量默认具有const属性:
通过下面代码来了解一下捕获列表的作用
int x = 0;
// 捕捉列表必须为空,因为全局变量不用捕捉就可以用,没有可被捕捉的变量
auto func1 = []()
{
x++;
};
int main()
{
// 只能用当前lambda局部域和捕捉的对象和全局对象
int a = 0, b = 1, c = 2, d = 3;
auto func1 = [a, &b]
{
// 值捕捉的变量不能修改,引用捕捉的变量可以修改
//a++; // 错误,不能修改
b++;
int ret = a + b;
return ret;
};
cout << func1() << endl;
// 隐式值捕捉
// 用了哪些变量就捕捉哪些变量
auto func2 = [=]
{
int ret = a + b + c;
return ret;
};
cout << func2() << endl;
// 隐式引用捕捉
// 用了哪些变量就捕捉哪些变量
auto func3 = [&]
{
a++;
c++;
d++;
};
func3();
cout << a << " " << b << " " << c << " " << d << endl;
// 混合捕捉1
auto func4 = [&, a, b]
{
//a++;
//b++;
c++;
d++;
return a + b + c + d;
};
func4();
cout << a << " " << b << " " << c << " " << d << endl;
// 混合捕捉1
auto func5 = [=, &a, &b]
{
a++;
b++;
/*c++;
d++;*/
return a + b + c + d;
};
func5();
cout << a << " " << b << " " << c << " " << d << endl;
// 局部的静态和全局变量不能捕捉,也不需要捕捉
static int m = 0;
auto func6 = []
{
int ret = x + m;
return ret;
};
// 传值捕捉本质是一种拷⻉,并且被const修饰了
// mutable相当于去掉const属性,可以修改了
// 但是修改了不会影响外面被捕捉的值,因为是一种拷⻉
auto func7 = [=]()mutable
{
a++;
b++;
c++;
d++;
return a + b + c + d;
};
cout << func7() << endl;
cout << a << " " << b << " " << c << " " << d << endl;
return 0;
}运行结果:

在 C++11 引入 lambda 表达式之前,开发者主要依靠函数指针和仿函数(函数对象) 来实现可调用行为。这两种传统方式各有其局限性,而 lambda 表达式则提供了一种革命性的替代方案,极大地简化了代码并提升了开发效率。
// 1. 定义函数原型
void process_element(int value, double factor);
// 2. 定义函数实现
void process_element(int value, double factor) {
std::cout << value * factor;
}
// 3. 使用函数指针
void (*func_ptr)(int, double) = &process_element;
func_ptr(10, 2.5); // 调用问题:
// 1. 定义完整的类
class Multiplier {
double factor_;
public:
Multiplier(double factor) : factor_(factor) {}
// 2. 重载函数调用运算符
void operator()(int value) const {
std::cout << value * factor_;
}
};
// 3. 使用仿函数
Multiplier multiplier(2.5);
multiplier(10); // 调用问题:
// 直接在调用点定义逻辑
auto process = [factor = 2.5](int value) {
std::cout << value * factor;
};
process(10); // 调用优势:
// 编译器自动推导返回类型
auto square = [](auto x) { return x * x; };
// 支持多种类型
std::cout << square(5); // int: 25
std::cout << square(2.5); // double: 6.25int base = 100;
double factor = 1.5;
// 捕获局部变量
auto calculator = [base, &factor](int value) {
factor += 0.1; // 修改引用捕获的变量
return (base + value) * factor;
};
std::cout << calculator(50); // (100+50)*1.6 = 240// 创建线程并定义执行逻辑
std::thread worker([task_id = 42] {
std::cout << "Starting task " << task_id << "\n";
perform_computation();
std::cout << "Completed task " << task_id << "\n";
});
worker.join();优势:
// 文件资源管理
auto file_deleter = [](FILE* f) {
if (f) {
std::cout << "Closing file\n";
fclose(f);
}
};
// 使用lambda作为删除器
std::unique_ptr<FILE, decltype(file_deleter)>
file_ptr(fopen("data.txt", "r"), file_deleter);对比传统方式:
// 传统函数指针方式
void file_deleter_func(FILE* f) { /*...*/ }
std::unique_ptr<FILE, void(*)(FILE*)>
ptr(fopen(...), &file_deleter_func);std::vector<Person> people;
// 按年龄排序
std::sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
// 查找特定条件的人
auto it = std::find_if(people.begin(), people.end(),
[](const Person& p) {
return p.name.starts_with("A") && p.age > 30;
});// 提交异步任务
auto future = std::async(std::launch::async,
[url = "https://example.com"] {
return fetch_data(url); // 捕获URL
});
// 获取结果
auto result = future.get();// 按钮点击事件处理
button.on_click([&counter, this](const Event& e) {
counter++; // 捕获计数器引用
update_display(); // 捕获this指针
log_event(e); // 访问事件对象
});// 传统C风格回调
void register_callback(void (*callback)(int, void*), void* data);
// 使用lambda封装状态
int state = 42;
register_callback([](int result, void* data) {
int* s = static_cast<int*>(data);
std::cout << "Result: " << result << ", State: " << *s;
}, &state);
// C++11后更优雅的方式
std::function<void(int)> callback = [&state](int result) {
std::cout << "Result: " << result << ", State: " << state;
};
register_cpp_callback(callback);• lambda的实现机制与范围for循环类似,从汇编指令层面来看,lambda和范围for都不存在实际指令。范围for底层通过迭代器实现,而lambda则通过仿函数对象实现。当我们编写一个lambda表达式时,编译器会自动生成对应的仿函数类。
• 编译器按照特定规则生成仿函数类名,确保每个lambda表达式对应唯一类名。lambda的参数、返回类型和函数体分别对应仿函数operator()的参数、返回类型和函数体实现。捕获列表中的变量会转化为仿函数类的成员变量,这些变量实际上是lambda类构造函数的参数。对于隐式捕获的情况,编译器会自动分析并传递实际使用的对象。
• 上述实现原理可以通过分析汇编代码来验证,后文展示的第二段汇编代码将证实这一机制的实现细节。
如下代码所示:
class Rate
{
public:
Rate(double rate)
: _rate(rate)
{}
double operator()(double money, int year)
{
return money * _rate * year;
}
private:
double _rate;
};
int main()
{
double rate = 0.49;
// lambda
auto r2 = [rate](double money, int year)
{
return money * rate * year;
};
// 函数对象
Rate r1(rate);
r1(10000, 2);
r2(10000, 2);
return 0;
}在汇编代码中:
// lambda
auto r2 = [rate](double money, int year)
{
return money * rate * year;
};
// 捕捉列表的rate,可以看到作为lambda_1类构造函数的参数传递了,这样要拿去初始化成员变量
// 下面operator()中才能使用
00D8295C lea eax, [rate]
00D8295F push eax
00D82960 lea ecx, [r2]
00D82963 call `main'::`2': : <lambda_1>::<lambda_1> (0D81F80h)
// 函数对象
Rate r1(rate);
00D82968 sub esp, 8
00D8296B movsd xmm0, mmword ptr[rate]
00D82970 movsd mmword ptr[esp], xmm0
00D82975 lea ecx, [r1]
00D82978 call Rate::Rate(0D81438h)
r1(10000, 2);
00D8297D push 2
00D8297F sub esp, 8
00D82982 movsd xmm0, mmword ptr[__real@40c3880000000000(0D89B50h)]
00D8298A movsd mmword ptr[esp], xmm0
00D8298F lea ecx, [r1]
00D82992 call Rate::operator() (0D81212h)
// 汇编层可以看到r2 lambda对象调用本质还是调用operator(),类型是lambda_1,这个类型名
// 的规则是编译器自己定制的,保证不同的lambda不冲突
r2(10000, 2);
00D82999 push 2
00D8299B sub esp, 8
00D8299E movsd xmm0, mmword ptr[__real@40c3880000000000(0D89B50h)]
00D829A6 movsd mmword ptr[esp], xmm0
00D829AB lea ecx, [r2]
00D829AE call `main'::`2': : <lambda_1>::operator() (0D824C0h)Lambda 表达式会被转换为匿名函数对象:
// 原始lambda
auto lambda = [](int x) { return x * 2; };
// 编译器生成的等价类
class __AnonymousLambda {
public:
auto operator()(int x) const {
return x * 2;
}
};int base = 100;
// 值捕获
auto add_val = [base](int x) { return x + base; };
/* 等价类 */
class __AddValLambda {
int base; // 值捕获的副本
public:
__AddValLambda(int b) : base(b) {}
auto operator()(int x) const { return x + base; }
};
// 引用捕获
auto add_ref = [&base](int x) { return x + base; };
/* 等价类 */
class __AddRefLambda {
int& base; // 引用捕获
public:
__AddRefLambda(int& b) : base(b) {}
auto operator()(int x) const { return x + base; }
};template <class T>
class function; // 未定义的通用模板
template <class Ret, class... Args>
class function<Ret(Args...)>; // 特化版本std::function 是一个通用的函数包装器模板类,属于C++11标准库的一部分。它提供了一种类型安全的方式来存储、复制和调用各种可调用对象(callable objects)。
包装能力:可以包装存储以下类型的可调用对象:
int(*)(int, int))状态检测:
头文件:
#include <functional>std::function<int(int, int)> func; // 声明一个接受两个int参数,返回int的function对象
func = [](int a, int b){ return a + b; }; // 包装lambda表达式
int result = func(2, 3); // 调用存储的可调用对象std::map<std::string, std::function<void()>> command_map;
command_map["start"] = [](){ std::cout << "Starting...\n"; };
command_map["stop"] = [](){ std::cout << "Stopping...\n"; };
std::string cmd = "start";
if(command_map.find(cmd) != command_map.end()) {
command_map[cmd](); // 调用对应的函数
}参考文档: std::function - cppreference.com
int f(int a, int b)
{
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
class Plus
{
public:
Plus(int n = 10)
:_n(n)
{}
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return (a + b) * _n;
}
private:
int _n;
};
int main()
{
// 包装各种可调用对象
function<int(int, int)> f1 = f;
function<int(int, int)> f2 = Functor();
function<int(int, int)> f3 = [](int a, int b) {return a + b; };
cout << f1(1, 1) << endl;
cout << f2(1, 1) << endl;
cout << f3(1, 1) << endl;
// 包装静态成员函数
// 成员函数要指定类域并且前面加&才能获取地址
function<int(int, int)> f4 = &Plus::plusi;
cout << f4(1, 1) << endl;
// 包装普通成员函数
// 普通成员函数还有一个隐含的this指针参数,所以绑定时传对象或者对象的指针过去都可以
function<double(Plus*, double, double)> f5 = &Plus::plusd;
Plus pd;
cout << f5(&pd, 1.1, 1.1) << endl;
function<double(Plus, double, double)> f6 = &Plus::plusd;
cout << f6(pd, 1.1, 1.1) << endl;
cout << f6(Plus(), 1.1, 1.1) << endl;
function<double(Plus&&, double, double)> f7 = &Plus::plusd;
cout << f7(move(pd), 1.1, 1.1) << endl;
cout << f7(Plus(), 1.1, 1.1) << endl;
return 0;
}运行结果:

这里再提一下,静态成员函数也可以不加&
// 包装静态成员函数
function<int(int, int)> f4 = Plus::plusi;
cout << f4(1, 1) << endl;template <class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);template <class Ret, class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);bind 是一个功能强大的函数模板,属于可调用对象包装器范畴。它本质上是一个函数适配器,允许开发者对输入的可调用对象 fn 进行参数绑定和调整,最终返回一个新的可调用对象。bind 的主要功能包括:
该模板位于 <functional> 头文件中,是标准库提供的重要功能组件。
典型的 bind 调用形式如下:
auto newCallable = bind(callable, arg_list);其中:
newCallable 是生成的新可调用对象callable 是原始可调用对象(函数、函数指针、成员函数、函数对象等)arg_list 是以逗号分隔的参数列表,对应 callable 的参数当调用 newCallable 时,它会自动调用原始 callable,并传递 arg_list 中绑定的参数。
arg_list 中可以包含特殊形式的占位符:
_n其中 n 是正整数(如 _1, _2, _3 等),这些占位符表示:
newCallable 的调用者提供n 表示占位参数在 newCallable 参数列表中的位置: _1 对应 newCallable 的第一个参数_2 对应第二个参数这些占位符定义在 std::placeholders 命名空间中,使用时需要:
using namespace std::placeholders;
// 或者
using std::placeholders::_1;
using std::placeholders::_2;
// ..._1 开始,而不是 _0示例:
#include <functional>
#include <iostream>
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int a, int b)
{
return (a - b) * 10;
}
int SubX(int a, int b, int c)
{
return (a - b - c) * 10;
}
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl;
// bind 本质返回的一个仿函数对象
// 调整参数顺序(不常用)
// _1代表第一个实参
// _2代表第二个实参
// ...
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl;
// 调整参数个数(常用)
auto sub3 = bind(Sub, 100, _1);
cout << sub3(5) << endl;
auto sub4 = bind(Sub, _1, 100);
cout << sub4(5) << endl;
// 分别绑死第123个参数
auto sub5 = bind(SubX, 100, _1, _2);
cout << sub5(5, 1) << endl;
auto sub6 = bind(SubX, _1, 100, _2);
cout << sub6(5, 1) << endl;
auto sub7 = bind(SubX, _1, _2, 100);
cout << sub7(5, 1) << endl;
// 成员函数对象进行绑死,就不需要每次都传递了
function<double(Plus&&, double, double)> f6 = &Plus::plusd;
Plus pd;
cout << f6(move(pd), 1.1, 1.1) << endl;
cout << f6(Plus(), 1.1, 1.1) << endl;
// bind一般用于,绑死一些固定参数
function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f7(1.1, 1.1) << endl;
return 0;
}std::bind 基本用法auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl; // 输出: (10-5)*10 = 50sub1(10, 5) → Sub(10, 5)
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl; // 输出: (5-10)*10 = -50sub2(10, 5) → Sub(5, 10)
_2 对应第一个实参10,_1 对应第二个实参5
如图所示:

auto sub3 = bind(Sub, 100, _1);
cout << sub3(5) << endl; // 输出: (100-5)*10 = 950
auto sub4 = bind(Sub, _1, 100);
cout << sub4(5) << endl; // 输出: (5-100)*10 = -950sub3(5) → Sub(100, 5)
sub4(5) → Sub(5, 100)
auto sub5 = bind(SubX, 100, _1, _2);
cout << sub5(5, 1) << endl; // 输出: (100-5-1)*10 = 940
auto sub6 = bind(SubX, _1, 100, _2);
cout << sub6(5, 1) << endl; // 输出: (5-100-1)*10 = -960
auto sub7 = bind(SubX, _1, _2, 100);
cout << sub7(5, 1) << endl; // 输出: (5-1-100)*10 = -960std::functionfunction<double(Plus&&, double, double)> f6 = &Plus::plusd;double(Plus&&, double, double)
Plus 对象作为第一个参数
Plus pd;
cout << f6(move(pd), 1.1, 1.1) << endl; // 输出: 2.2
cout << f6(Plus(), 1.1, 1.1) << endl; // 输出: 2.2std::move)
std::bind 绑定对象function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f7(1.1, 1.1) << endl; // 输出: 2.2bind 固定了 Plus 对象(临时对象)
std::bind 的返回值// bind 本质返回的一个仿函数对象
auto sub1 = bind(Sub, _1, _2);std::bind 返回一个未指定类型的函数对象
auto 接收返回值
// 非静态成员函数需要对象上下文
bind(&Plus::plusd, Plus(), _1, _2)&Plus::plusd
Plus() 创建临时对象作为调用上下文
方式 | 示例 | 生命周期 |
|---|---|---|
值传递(临时对象) | bind(&T::func, T(), ...) | 绑定对象在函数对象内 |
引用传递 | bind(&T::func, ref(obj), ...) | 依赖外部对象生命周期 |
指针传递 | bind(&T::func, &obj, ...) | 依赖外部对象生命周期 |
移动语义 | bind(&T::func, move(obj), ...) | 转移所有权到绑定对象 |
运行结果:
