【lambda表达式 + 包装器】目录
- 前言:
- [------------ lambda表达式 ------------](#------------ lambda表达式 ------------)
-
- [1. 什么是lambda表达式?](#1. 什么是lambda表达式?)
- [2. lambda表达式的基本语法形式?](#2. lambda表达式的基本语法形式?)
- [3. lambda表达式怎么使用?](#3. lambda表达式怎么使用?)
- [4. 特殊的捕获限制有哪些?](#4. 特殊的捕获限制有哪些?)
- [5. lambda表达式的价值是什么?](#5. lambda表达式的价值是什么?)
- [6. lambda表达式的应用有哪些?](#6. lambda表达式的应用有哪些?)
- [7. lambda表达式的原理是什么?](#7. lambda表达式的原理是什么?)
- [------------ 包装器 ------------](#------------ 包装器 ------------)
-
- [1. 什么是包装器?](#1. 什么是包装器?)
- [2. 常见的包装器有哪些?](#2. 常见的包装器有哪些?)
- [3. 包装器怎么应用?](#3. 包装器怎么应用?)

往期《C++初阶》回顾:
往期《C++进阶》回顾:
/------------ 继承多态 ------------/
【普通类/模板类的继承 + 父类&子类的转换 + 继承的作用域 + 子类的默认成员函数】
【final + 继承与友元 + 继承与静态成员 + 继承模型 + 继承和组合】
【多态:概念 + 实现 + 拓展 + 原理】/------------ STL ------------/
【二叉搜索树】
【AVL树】
【红黑树】
【set/map 使用介绍】
【set/map 模拟实现】
【哈希表】
【unordered_set/unordered_map 使用介绍】
【unordered_set/unordered_map 模拟实现】/------------ C++11 ------------/
【列表初始化 + 右值引用】
【移动语义 + 完美转发】
【可变参数模板 + emplace接口 + 新的类功能】
前言:
hi ~,小伙伴们大家好啊,今天是......嗯~o ( ̄▽ ̄) o 对,今天是国庆节!是不是没料到距离上次更新才过去两天,鼠鼠就又来啦?
哈哈,偷偷告诉你们一个小秘密:每月的一号鼠鼠都会准时更新哦~~(ノ>ω<)ノ你要是不信,那就罚你等到 11 月 1 号再来验证,到时候可别忘啦来!(≧▽≦)♪
今天我们要学习的新特性是:【lambda表达式 + 包装器】 。╰(▔∀▔)╯【lambda 表达式 + 包装器】就像给 "临时工具" 配 "专属收纳盒":→ (っ•̀ᴗ•́)っ✧
- lambda 是 C++ 里随写随用的 "临时函数",不用单独定义函数名,写在遍历排序、过滤等场景里很轻便,但类型隐蔽,没法直接传参或复用 ╮(╯▽╰)╭
- 包装器(比如 function)就是 "收纳盒",能把 lambda "装起来" 赋予明确类型,不管传参还是存起来反复用都能搞定,就像临时扳手放进工具盒,好拿好存不麻烦╰(✧∇✧)╯
简单说,lambda 负责 "快速造工具",包装器负责 "好好存工具",两者搭在一起,既能享受 lambda 的便捷,又能解决它的使用局限,写代码时灵活度和实用性直接拉满~ദ്ദി˶>ω<)✧
------------ lambda表达式 ------------
1. 什么是lambda表达式?
lambda表达式
:是一种匿名函数,也就是没有名字的函数。
它是一种简洁的函数定义方式,不需要显式命名函数
它可以方便地定义
简短的可调用对象
,用于各种需要函数对象
的场景可定义在
函数内部
(普通函数只能定义在全局
、命名空间
或类
中)语法层面无显式类型,需通过
auto
或模板参数
接收其匿名类型
主要特点:
- 匿名性:没有函数名
- 简洁性:通常用于简单操作,可以在一行内完成
- 临时性:常用于一次性使用或作为参数传递给高阶函数
2. lambda表达式的基本语法形式?
基本语法形式:
cpp[捕获列表](参数列表) -> 返回类型 { 函数体 } [capture-list] (parameters) -> return-type { function-body }
- 捕获列表 (
capture-list
):用于指定在 lambda 表达式中可以访问的外部变量,是 lambda 能够访问所在作用域中变量的关键。
- 必须出现在 lambda 最开头,编译器通过
[]
识别 lambda 开始- 它可以为空,表示不捕获任何外部变量,但是
[]
不能省略(空捕获列表是语法要求)- 参数列表 (
parameters
):和普通函数的参数列表类似,用于指定 lambda 函数的参数。
- 它可以为空
- 若无需参数传递,可连同
()
一起省略 (如:无参 lambda 可写[] { ... }
)- 返回值类型 (
return-type
):指定 lambda 函数的返回值类型。
- 无返回值时(如:
void
),可直接省略- 返回值类型明确时(如:单一
return
语句),编译器可自动推导,也可省略- 函数体 (
function-body
):定义 lambda 函数的具体操作和逻辑,也就是函数执行时要做的事情。
- 除参数外,可直接使用捕获列表中的变量
- 即使函数体为空(如:
[] {}
),{}
也不能省略
通过上面的关于lambda的语法的介绍,现在我们可以明白
空lambda
的形式了:
cpp// 空lambda(所有可省略部分均省略) auto emptyLambda = [] {}; // 合法(但无实际逻辑)
2.1:捕获列表
lambda表达式默认仅能访问自身函数体、参数列表里的变量。若需使用外层作用域(如:包含 lambda 的函数作用域)的变量,需通过 捕获列表 显式声明。
捕获列表决定了 lambda 表达式对外部变量的访问方式,常见的捕获方式有以下几种:
显示捕获
:在捕获列表中明确写出变量名,用逗号分隔多个变量,同时通过符号区分捕获方式:
值捕获:直接写变量名(如:
[x, y]
)引用捕获:变量名前加
&
(如:[&z]
)
隐式捕获
:无需逐个写变量名,用符号批量指定捕获规则,编译器会自动识别 lambda 内用到的外层变量并捕获:
- 按值隐式捕获:捕获列表写
[=]
,lambda 内用到的外层变量全部传值捕获(创建副本)- 按引用隐式捕获:捕获列表写
[&]
,lambda 内用到的外层变量全部传引用捕获(使用引用)
混合捕获
:同时用隐式捕获符号 (=
或&
)和显式变量。
值捕获
:在 lambda 内部对捕获变量的修改不会影响外部的原始变量。
- 例如 :
[a]
表示按值捕获变量a
cpp
#include <iostream>
using namespace std;
int main()
{
//1.定义一个局部变量,初始值为10
int num = 10;
//2.定义一个lambda表达式,通过值捕获方式获取外部变量num
auto lambda = [num]()mutable
{
//2.1:尝试修改捕获的副本(注意:修改的是副本,不是原始变量)
num = 20; //注意:此操作不会影响外部的num变量
//2.2:输出捕获副本的值(此时副本已被修改为20)
cout << num << endl; // 输出: 20
};
/* 注意事项:默认情况下,按值捕获的变量在lambda表达式内部是不可修改的(const)
*
* 1. 当你使用值捕获 [num] 时,lambda 内部得到的是 num 的一个副本,
* 2. 但这个副本默认是 const 的,不能修改。
* 3. 因此当你尝试在 lambda 内部修改 num 时,编译器会报错。
* 4. 要使按值捕获的变量可以在 lambda 内部修改,需要使用 mutable 关键字
*/
//3.调用lambda表达式,执行内部逻辑
lambda();
//4.输出外部num的值(未被lambda内部修改)
cout << num << endl; // 输出: 10
return 0;
}

引用捕获 :在 lambda 内部对捕获变量的修改会直接影响外部的原始变量。
- 例如 :
[&a]
,表示按引用捕获变量a
cpp
#include <iostream>
int main()
{
//1.定义一个局部变量 num
int num = 10;
//2.定义一个lambda表达式,使用引用捕获的方式捕获外部变量 num
auto lambda = [&num]()
{
//2.1:由于是引用捕获,这里对 num 的修改会直接反映到外部的 num 变量上
num = 20;
//2.2:输出修改后的 num 的值,此时会输出 20
std::cout << num << std::endl;
};
/* 注意事项:
* 1. [&num] 表示以引用的方式捕获变量 num,
* 2. 这意味着 lambda 表达式内部对 num 的操作会直接作用于外部定义的这个 num 变量
* 3. 因为它们共享同一块内存空间
*/
//3.调用定义好的lambda表达式,执行其内部的代码逻辑
lambda();
//4.再次输出 num 的值
std::cout << num << std::endl; //因为 lambda 表达式中通过引用捕获修改了 num,所以这里输出 20
return 0;
}

隐式捕获 :可以让编译器自动推导捕获列表。 分为:
- 按值隐式捕获:
[=]
- 按引用隐式捕获:
[&]
cpp
#include <iostream>
int main()
{
//1.定义两个局部变量
int num1 = 10;
int num2 = 20;
//2.lambda1:按值隐式捕获所有用到的外部变量
auto lambda1 = [=]()
{
//2.1:输出捕获的副本值
std::cout << num1 << " " << num2 << std::endl; // 输出: 10 20
};
/* 注意事项:
* [=]:表示以值捕获方式捕获所有外部变量
* lambda内部使用的是外部变量的副本,无法修改原始变量
*/
//3.lambda2:按引用隐式捕获所有用到的外部变量
auto lambda2 = [&]()
{
//3.1:直接修改外部的num1变量
num1 = 30;
//3.2:输出修改后的外部变量值
std::cout << num1 << " " << num2 << std::endl; // 输出: 30 20
};
/* 注意事项:
* [&]:表示以引用捕获方式捕获所有外部变量
* lambda内部使用的是外部变量的引用,可以直接修改原始变量
*/
//4.调用lambda1:输出捕获时的值副本
lambda1(); // 输出: 10 20
//5.调用lambda2:修改外部变量并输出
lambda2(); // 输出: 30 20
//6.验证外部变量确实被修改
std::cout << "外部num1: " << num1 << std::endl; // 输出: 30
return 0;
}

混合捕获 :可以同时使用值捕获和引用捕获。
- 若开头用
[=]
(隐式传值),后续显式变量必须传引用
- 如 :
[=, &x]
(其他变量隐式传值,x
显式传引用)- 若开头用
[&]
(隐式传引用),后续显式变量必须传值
- 如 :
[&, x]
(其他变量隐式传引用,x
显式传值)所以 :
[=, &a]
表示按值捕获除a
以外的所有用到的外部变量,而a
按引用捕获。
总结:捕获列表的常见形式
捕获列表写法 | 含义 | 示例 (外部变量 int a=10; double b=3.14; ) |
---|---|---|
[] |
不捕获任何外部变量 | lambda 内无法使用 a 、b |
[a] |
传值捕获 a (修改 lambda 内的 a 不影响外部) |
lambda 内 a 是 10 的副本 |
[&b] |
传引用捕获 b (修改 lambda 内的 b 会影响外部) |
lambda 内 b 是外部 b 的引用 |
[=] |
传值捕获所有用到的外部变量 | lambda 内 a 是副本,b 是副本 (修改不影响外部) |
[&] |
传引用捕获所有用到的外部变量 | lambda 内 a 、b 都是引用 (修改会影响外部) |
[a, &b] |
混合捕获 (a 传值,b 传引用) |
lambda 内 a 是副本,b 是引用 |
2.2:参数列表与返回值
参数列表:和普通函数一样,可以有不同类型的参数,也可以没有参数。
[]() { std::cout << "Hello Lambda!" << std::endl; }
没有参数,只是输出一条信息[](int a, int b) { return a + b; }
接收两个int
类型的参数并返回它们的和
返回值:
如果函数体只有一条返回语句,返回值类型可以省略,编译器会自动推导。
cpp[](int a, int b) //编译器会自动推导返回值类型为:int { return a + b; }
如果函数体有多条语句 且需要明确返回值类型,就必须显式指定。
cpp[](int a, int b) -> bool //注意:必须显式指定返回类型:bool { if (a > b) return true; else return false; }
3. lambda表达式怎么使用?
代码案例1:lambda表达式的使用
cpp
#include <iostream>
using namespace std;
int main()
{
/*------------------- 完整语法示例(显式声明所有部分)-------------------*/
auto add1 = [](int a, int b) -> int
{
return a + b;
};
/* 定义一个完整语法的lambda表达式:
* 1. [] 空捕获列表(不捕获任何外部变量)
* 2. (int a, int b) 参数列表,接收两个int类型参数
* 3. -> int 显式指定返回值类型为int
* 4. { return a + b; } 函数体,实现两数相加
*/
cout << "add1(3,4) = " << add1(3, 4) << endl;
/*------------------- 省略返回值类型(编译器自动推导)-------------------*/
auto add2 = [](int a, int b)
{
return a + b; // 返回值类型被自动推导为int
};
/* 省略返回值类型,编译器根据return语句自动推导返回类型为int
* 其他部分与add1完全相同
*/
cout << "add2(5,6) = " << add2(5, 6) << endl;
/*------------------- 省略参数列表(无参场景)-------------------*/
auto greet = []
{
std::cout << "Hello Lambda" << endl;
};
/* 省略参数列表的写法:当lambda不需要参数时,()可以连同参数一起省略
* 但[]和{}不能省略
*/
greet();
/*------------------- 省略捕获列表和参数列表(最简形式)-------------------*/
auto emptyLambda = [] {};
/* 最简lambda形式:空捕获列表+空参数列表
* 可以理解为一个无输入、无输出的函数
*/
emptyLambda(); //调用空lambda(无任何效果,但语法合法)
return 0;
}

代码案例2:lambda表达式的使用
cpp
#include <iostream>
using namespace std;
//定义全局变量 x
int x = 0;
int main()
{
// ====================== 1. "全局变量"与"空捕获列表" ======================
auto func1 = []() //注意:全局变量无需捕获即可使用,lambda 捕获列表必须为空
{
x++;
};
// ====================== 2. 显式捕获(传值 + 传引用) ======================
//1.定义局部变量 a,b,c,d
int a = 0, b = 1, c = 2, d = 3;
//2.显式捕获:a 传值(const 只读),b 传引用(可修改)
auto func2 = [a, &b]()
{
// a++; // 错误:传值捕获的变量默认 const,无法修改(除非加 mutable)
b++; // 正确:传引用捕获,可直接修改外部变量 b
int ret = a + b; // a=0(原值), b=2(已自增)
return ret;
};
cout << func2() << endl;
// ====================== 3. 隐式传值捕获([=]) ======================
//3.隐式传值捕获所有用到的变量(a, b, c)
auto func3 = [=]()
{
// a++; // 错误:传值捕获默认 const,无法修改
int ret = a + b + c; // a=0, b=2, c=2
return ret;
};
cout << func3() << endl;
// ====================== 4. 隐式传引用捕获([&]) ======================
//4.隐式传引用捕获所有用到的变量(a, c, d)
auto func4 = [&]()
{
a++; // 正确:传引用,修改外部 a → a=1
c++; // 正确:传引用,修改外部 c → c=3
d++; // 正确:传引用,修改外部 d → d=4
};
func4();
cout << a << " " << b << " " << c << " " << d << endl;
// ====================== 5. 混合捕获(隐式传引用 + 显式传值) ======================
//5. 隐式传引用(&) + 显式传值(a, b)
//规则:隐式符号(&)在前,显式变量必须传值
auto func5 = [&, a, b]()
{
// a++; // 错误:a 是显式传值,默认 const
// b++; // 错误:b 是显式传值,默认 const
c++; // 正确:隐式传引用,修改外部 c → c=4
d++; // 正确:隐式传引用,修改外部 d → d=5
return a + b + c + d; // 0 + 1 + 4 + 5 = 10
};
func5();
cout << a << " " << b << " " << c << " " << d << endl;
// ====================== 6. 混合捕获(隐式传值 + 显式传引用) ======================
//6.隐式传值(=) + 显式传引用(&a, &b)
//规则:隐式符号(=)在前,显式变量必须传引用
auto func6 = [=, &a, &b]()
{
a++; // 正确:显式传引用,修改外部 a → a=2
b++; // 正确:显式传引用,修改外部 b → b=3
// c++; // 错误:c 是隐式传值,默认 const
// d++; // 错误:d 是隐式传值,默认 const
return a + b + c + d; // 2 + 3 + 4 + 5 = 14
};
func6();
cout << a << " " << b << " " << c << " " << d << endl;
// ====================== 7. "静态变量"与"全局变量"的访问 ======================
//静态局部变量:无需捕获即可使用
static int m = 0;
auto func7 = []()
{
int ret = x + m; // 全局变量 x=0,静态变量 m=0
return ret; // 返回 0 + 0 = 0
};
cout << func7() << endl;
// ====================== 8. mutable 修饰符(突破传值捕获的 const 限制) ======================
//隐式传值捕获([=]) + mutable:允许修改传值副本(不影响外部变量)
auto func8 = [=]() mutable
{
a++; // 修改传值副本 a → 副本 a=3(外部 a 仍为 2)
b++; // 修改传值副本 b → 副本 b=4(外部 b 仍为 3)
c++; // 修改传值副本 c → 副本 c=5(外部 c 仍为 4)
d++; // 修改传值副本 d → 副本 d=6(外部 d 仍为 5)
return a + b + c + d; // 3 + 4 + 5 + 6 = 18
};
//输出内部变量的和
cout << func8() << endl; // 输出: 18
//输出外部变量
cout << a << " " << b << " " << c << " " << d << endl; //a=2, b=3, c=4, d=5(未被修改)
return 0;
}

4. 特殊的捕获限制有哪些?
lambda表达式定义的位置:
若lambda 若定义在函数局部作用域(如:main 函数内 ):
- 仅能捕获 lambda 定义位置之前 的局部变量
- 静态局部变量 (static int var; )和 全局变量 无需捕获,可直接在 lambda 内使用(因为:它们的作用域不受函数栈限制 )
若 lambda 定义在全局作用域(函数外 ):
- 捕获列表必须为空(无外层局部变量可捕获 )
mutable修饰符:突破传值捕获的只读限制:
默认情况下,传值捕获 的变量在 lambda 内是
const
只读的若需修改传值捕获的副本,可在参数列表后加
mutable
,此时参数列表不能省略 (即使无参数,也要写 () )
5. lambda表达式的价值是什么?
lambda表达式通过
简洁语法
实现了匿名函数对象
,核心价值在于:
- 支持函数内部定义,灵活捕获局部变量
- 语法各部分可按需省略,适配不同场景(如:无参、无返回值等)
- 配合 auto 使用,无需显式定义函数类型,让代码更简洁
掌握 lambda 的语法结构和捕获规则,是高效使用 C++ 现代特性(如:算法库、异步编程)的基础。
6. lambda表达式的应用有哪些?
在学习 lambda 表达式之前,我们所使用的
可调用对象
只有函数指针
和仿函数对象
- 函数指针的类型定义较为繁琐
- 仿函数需要定义一个类,操作相对麻烦
而使用 lambda 来定义可调用对象,简单又便捷。
lambda 在很多场景中也十分好用:
- 在线程里定义线程的执行函数逻辑
- 在智能指针中定制删除器等
lambda 的应用场景广泛,后续我们会不断接触到相关用法。
代码案例:lambda表达式应用的优越性(与传统的仿函数进行对比)
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
/*----------------------"商品结构体"定义----------------------*/
struct Goods
{
/*------------成员变量------------*/
string _name; // 商品名称
double _price; // 商品价格
int _evaluate; // 商品评价
/*------------成员函数------------*/
// 1.构造函数:初始化商品信息
Goods(const char* str, double price, int evaluate)
: _name(str)
, _price(price)
, _evaluate(evaluate)
{
}
};
/*----------------------"打印商品信息的函数对象"----------------------*/
struct PrintGoods
{
void operator()(const Goods& goods) const
{
cout << "名称:" << goods._name
<< ",价格:" << goods._price
<< ",评价:" << goods._evaluate
<< endl;
}
};
/*----------------------仿函数:价格升序比较(less)----------------------*/
struct ComparePriceLess
{
bool operator()(const Goods& gl, const Goods& gr) const
{
return gl._price < gr._price; // 价格低的排前面
}
};
/*----------------------仿函数:价格降序比较(greater)----------------------*/
struct ComparePriceGreater
{
bool operator()(const Goods& gl, const Goods& gr) const
{
return gl._price > gr._price; // 价格高的排前面
}
};
int main()
{
// 1.初始化商品列表
vector<Goods> v =
{
{"苹果", 2.1, 5}, // 商品1
{"香蕉", 3, 4}, // 商品2
{"橙子", 2.2, 3}, // 商品3
{"菠萝", 1.5, 4} // 商品4
};
//2.先输出初始商品列表
cout << "初始商品列表:" << endl;
for_each(v.begin(), v.end(), PrintGoods());
cout << "------------------------" << endl;
// -------------------- "仿函数"排序 --------------------
// 1. 按价格升序排序
sort(v.begin(), v.end(), ComparePriceLess());
cout << "按价格升序排序(仿函数)结果:" << endl;
for_each(v.begin(), v.end(), PrintGoods());
cout << "------------------------" << endl;
// 2. 按价格降序排序
sort(v.begin(), v.end(), ComparePriceGreater());
cout << "按价格降序排序(仿函数)结果:" << endl;
for_each(v.begin(), v.end(), PrintGoods());
cout << "------------------------" << endl;
// -------------------- "lambda表达式"排序 --------------------
// 优势:无需定义额外类,直接在排序处写比较逻辑
// 1. 按价格升序排序
sort(v.begin(), v.end(),
[](const Goods& g1, const Goods& g2) // lambda 替代 ComparePriceLess
{
return g1._price < g2._price;
});
cout << "按价格升序排序(lambda)结果:" << endl;
for_each(v.begin(), v.end(), PrintGoods());
cout << "------------------------" << endl;
// 2. 按价格降序排序
sort(v.begin(), v.end(),
[](const Goods& g1, const Goods& g2) // lambda 替代 ComparePriceGreater
{
return g1._price > g2._price;
});
cout << "按价格降序排序(lambda)结果:" << endl;
for_each(v.begin(), v.end(), PrintGoods());
cout << "------------------------" << endl;
// 3. 按评价升序排序(新需求,仿函数需额外定义类,lambda 直接写)
sort(v.begin(), v.end(),
[](const Goods& g1, const Goods& g2)
{
return g1._evaluate < g2._evaluate;
});
cout << "按评价升序排序(lambda)结果:" << endl;
for_each(v.begin(), v.end(), PrintGoods());
cout << "------------------------" << endl;
// 4. 按评价降序排序(新需求,lambda 直接写)
sort(v.begin(), v.end(),
[](const Goods& g1, const Goods& g2)
{
return g1._evaluate > g2._evaluate;
});
cout << "按评价降序排序(lambda)结果:" << endl;
for_each(v.begin(), v.end(), PrintGoods());
cout << "------------------------" << endl;
return 0;
}

7. lambda表达式的原理是什么?
lambda
的原理和范围for
类似,从编译后的汇编指令层面来看,不存在 lambda 和范围for 这些语法结构。
范围for
底层基于迭代器
实现,而lambda
底层实则是仿函数对象
,即我们编写一个 lambda 后,编译器会自动生成一个对应的仿函数类。
- 仿函数的类名由编译器按特定规则生成,确保不同 lambda 生成的类名互不相同
- lambda 的
参数
---> 对应仿函数operator()的参数
- lambda 的
返回类型与函数体
---> 对应仿函数返回类型与函数体
- lambda 的
捕获列表
---> 本质是生成的仿函数类的成员变量
也就是说,捕获列表里的变量会作为 lambda 类构造函数的实参,对于隐式捕获,编译器会依据实际使用的变量来传递相应对象。
关于上述原理,我们可通过汇编层进一步了解,下方的汇编代码可印证这些原理。
代码案例:根据lambda表达式和仿函数的关系阐述lambda表达式的本质
cpp
#include <iostream>
using namespace std;
// ====================== 仿函数类:Rate ======================
class Rate
{
private:
double _rate; // 存储利率的成员变量
public:
//1.实现:"构造函数"
Rate(double rate)
: _rate(rate) // 用传入的利率初始化成员变量
{ }
//2.实现:"重载 operator()"
double operator()(double money, int year) //目的:实现计算逻辑
{
//功能:计算利息 = 本金 * 利率 * 年限
return money * _rate * year;
}
};
int main()
{
//1.定义利率(后续用于仿函数和 lambda)
double rate = 0.49;
// ====================== lambda 表达式 ======================
auto func1 = [rate](double money, int year)
{
return money * rate * year;
};
/*---------------------- 调用"仿函数对象"----------------------*/
//1.首先用"仿函数类rate"初始化"仿函数对象func2"
Rate func2(rate);
//2.然后调用仿函数
cout << func2(10000, 2) << endl; //计算 10000 本金,2 年的利息
/*---------------------- 调用"lambda对象"----------------------*/
//1.调用lambda对象:
cout << func1(10000, 2) << endl; //计算 10000 本金,2 年的利息
return 0;
}
lambda 的底层是仿函数类:

"匿名仿函数类" 与 "显示仿函数类Rate" 的对比:
cpp
// 手动定义的仿函数类
class Rate { ... };
// 编译器为 lambda 生成的匿名类(逻辑等价)
class __lambda_xxxx
{
private:
double _rate; // 对应捕获的 rate
public:
//1.实现:"构造函数"
__lambda_xxxx(double rate)
: _rate(rate)
{}
//2.实现:"重载 operator()"
double operator()(double money, int year)
{
return money * _rate * year;
}
};
总结:"lambda" 与 "仿函数" 的关系
特性 | 手动定义的仿函数(Rate) | 编译器生成的 lambda 类 |
---|---|---|
类名 | 显式定义(Rate) | 匿名(由编译器生成唯一名称) |
成员变量 | 手动声明(_rate) | 自动捕获外部变量 |
operator () 逻辑 | 手动实现 | 由 lambda 函数体自动生成 |
调用方式 | 对象调用(func2 (...) ) |
同仿函数(func1 (...) ) |
lambda表达式的本质:
lambda 是 "语法糖",底层完全基于仿函数实现
编写 lambda 时,编译器会自动生成等价的仿函数类,简化了手动定义仿函数的繁琐过程
------------ 包装器 ------------
1. 什么是包装器?
包装器(Wrapper)
:是一种能对其他对象、函数、数据等进行封装的机制,目的是简化使用、统一接口或添加额外功能
- 它就像给原本的东西套了一层 "壳",让操作更方便
- 常见的包装器有
std::function
、std::bind
以及智能指针
(某种意义上也算资源包装器 )
包装器的核心作用:
- 统一接口 :把不同类型的可调用对象(函数指针、仿函数、lambda 等)"包装" 成相同类型,方便存储、传递。
- 比如 :lambda 是匿名类型,直接用很难统一管理,但
std::function
能把它们包装成一致的类型- 简化使用:隐藏底层复杂细节,调用时不用关心被包装对象的具体类型,按统一方式调用即可。
- 扩展功能 :可以在包装器里添加额外逻辑(比如:日志、异常处理 ),被包装的对象无需修改。
2. 常见的包装器有哪些?
常见的包装器有:
- 函数包装器(如 :
std::function
)- 绑定器(如 :
std::bind
)- 智能指针(如 :
std::unique_ptr
、std::shared_ptr
)
2.1:函数包装器(std::function)
C++ 中通过类模板实现
std::function
,核心原型简化如下:(标准库简化)
cpp
// 前向声明基础模板(未定义具体逻辑)
template <class T>
class function;
// 特化模板:用于匹配"返回值为 Ret、参数为 Args..."的可调用对象
template <class Ret, class... Args>
class function<Ret(Args...)>;
std::function
:是 C++ 标准库<functional>
里的类模板
,专门用来包装可调用对象 (函数指针
、仿函数
、lambda
、std::bind结果
等 ),使它们具有统一的调用方式,从而方便存储、传递和管理。
std::function 是可调用对象的 "包装器",关键特性:
- 支持可调用对象 :
能包裹函数指针、仿函数(函数对象)、lambda 表达式、std::bind
绑定的表达式等- "空" 状态与异常 :
若std::function
未绑定任何可调用对象(空状态 ),调用时会抛出std::bad_function_call
异常- 统一类型抽象 :
无论原始可调用对象类型多复杂(如 :lambda 是匿名类型 ),都能被std::function
包装为相同类型,便于声明和使用
std::function的类型声明:
cppfunction<int(int, int)>
- 这里
std::function<int(int, int)>
就是 "包装器类型"- 模板参数
<int(int, int)>
表示:返回值为int
,接受两个int
参数的可调用对象- 可简化理解为:
function<返回类型(参数类型列表)>
意义:不管底层是函数指针、仿函数还是 lambda,都被包装成相同类型,调用方式完全一致。
代码示例1 :用
std::function
统一包装不同可调用对象
cpp
#include <iostream>
#include <functional>
using namespace std;
//1.实现:两数相加的"普通函数"
int add(int a, int b)
{
return a + b;
}
//2.实现:两数相乘的"仿函数"(函数对象)
struct Multiplier
{
//重载函数调用运算符,使对象可像函数一样调用
int operator()(int a, int b) const
{
return a * b;
}
};
int main()
{
// ==================== std::function 包装不同类型的可调用对象 ====================
//1.包装普通函数:将函数 add 包装为 std::function 类型
function<int(int, int)> func1 = add;
//2.包装仿函数:实例化 Multiplier 对象并包装
function<int(int, int)> func2 = Multiplier();
//3.包装lambda表达式:将匿名lambda函数包装为 std::function 类型
function<int(int, int)> func3 = [](int a, int b)
{
return a - b;
};
// ==================== 统一调用方式,无需关心底层实现 ====================
//1.调用"普通函数包装器"
cout << "func1(3, 4) = " << func1(3, 4) << endl; // 输出 7(3+4)
//2.调用"仿函数包装器"
cout << "func2(3, 4) = " << func2(3, 4) << endl; // 输出 12(3*4)
//3.调用"lambda包装器"
cout << "func3(3, 4) = " << func3(3, 4) << endl; // 输出 -1(3-4)
return 0;
}

不同可调用对象的不同包装方式:
普通函数 :直接使用
函数名
(函数指针)仿函数 :需要先
实例化对象
(Multiplier()
),再进行包装lambda表达式 :直接使用
匿名lambda函数
cpp
// ==================== std::function 包装不同类型的可调用对象 ====================
//1.包装普通函数:将函数 add 包装为 std::function 类型
function<int(int, int)> func1 = add;
//2.包装仿函数:实例化 Multiplier 对象并包装
function<int(int, int)> func2 = Multiplier();
//3.包装lambda表达式:将匿名lambda函数包装为 std::function 类型
function<int(int, int)> func3 = [](int a, int b)
{
return a - b;
};
不同可调用对象的统一调用语法:
cppfunc1(3, 4); // 无论底层是函数、仿函数还是 lambda,调用方式完全一致
意义 :体现了
std::function
的核心价值:类型擦除,统一接口
std::function
内部通过类型擦除技术,封装了不同类型的可调用对象,对外提供统一的调用接口std::function
存在一定的性能开销(虚函数调用),对于性能敏感场景,可考虑使用函数指针或模板
代码示例2 :用
std::function
包装"静态成员函数"和"普通成员函数"
cpp
#include<iostream>
#include<functional>
using namespace std;
//实现:包含成员函数的类 Plus
class Plus
{
private:
/*-----------------成员变量-----------------*/
int _n; // 成员变量,影响 plusd 的计算结果
public:
//1.实现:"构造函数"
Plus(int n = 10)
: _n(n)
{ }
//2.实现:"静态成员函数"
static int staticFunc(int a, int b)
{
return a + b; //(无依赖对象状态)
}
//3.实现:"普通成员函数"
double Func(double a, double b)
{
return (a + b) * _n; //(依赖对象的 _n 值)
}
};
int main()
{
// ====================== 1. 包装"静态成员函数" ======================
/********************* 静态成员函数无隐含 this 指针,直接取地址即可 *********************/
function<int(int, int)> f4 = &Plus::staticFunc;
cout << f4(1, 1) << endl; // 等价于调用 Plus::staticFunc(1,1),结果 2
// ====================== 2. 包装"普通成员函数" ======================
/********************* 普通成员函数有隐含 this 指针,需指定"对象"或"对象指针"调用 *********************/
Plus pd; // 创建 Plus 对象
//1.用对象指针绑定(this 指针为 &pd)
function<double(Plus*, double, double)> f5 = &Plus::Func;
cout << f5(&pd, 1.1, 1.1) << endl; // 调用:pd.Func(1.1, 1.1) → (1.1+1.1)*10(_n 默认为 10)
//2.用对象值绑定(this 指针为 pd 的拷贝)
function<double(Plus, double, double)> f6 = &Plus::Func;
cout << f6(pd, 1.1, 1.1) << endl; //调用:pd.Func(1.1, 1.1) → 同样 (2.2)*10
//3.用右值引用绑定(this 指针为临时对象)
function<double(Plus&&, double, double)> f7 = &Plus::Func;
//3.1:move(pd)将 pd 转为右值,调用临时对象的 Func
cout << f7(move(pd), 1.1, 1.1) << endl;
//3.2:直接构造临时对象 Plus(),调用其 Func
cout << f7(Plus(), 1.1, 1.1) << endl;
return 0;
}

2.2:绑定器(std::bind)
std::bind
有两种典型声明形式:(标准库简化)
cpp
// 形式 1:自动推导返回类型
template <class Fn, class... Args>
auto bind(Fn&& fn, Args&&... args);
// 形式 2:显式指定返回类型(较少直接使用)
template <class Ret, class Fn, class... Args>
auto bind(Fn&& fn, Args&&... args);
std::bind
:是定义在<functional>
头文件中的函数模板
,它的主要作用是对函数
或可调用对象的参数
进行绑定 、重新排列 ,生成一个新的可调用对象。
std::bind 像一个可调用对象的 "函数适配器",支持:
- 固定参数:把可调用对象的某些参数 "固定" 为具体值,减少调用时传递的参数。
- 调整参数顺序:重新排列可调用对象的参数顺序。
- 参数占位 :用占位符(如 :
_1
、_2
)预留参数位置,让新的可调用对象能接收动态参数。
std::bind的类型声明:
cpp// 调用格式: auto newCallable = bind(callable, arg_list); // 实际运用: auto newFunc = bind(multiply, 2, placeholders::_1, placeholders::_2);
- 第一个参数:****
callable
:必须是被包装的可调用对象(如 :multiply
)- 后续参数:****
arg_list
:参数列表,支持两种元素
- 具体值 :(如 :
2
)会固定 callable 对应参数的位置(这里固定a=2
,不清楚的话可以先看一下下面的代码)- 占位符 :(如 :
_1
、_2
)(来自std::placeholders
命名空间 ),表示新可调用对象 newCallable 的参数位置- 赋值符左侧的:****
newCallable
:返回的新可调用对象,调用它时,会触发原始callable
执行,并传递arg_list
中配置的参数(包括占位符对应的动态参数 )总结 :
std::bind
的第一个参数是要绑定的可调用对象,后续参数可以是具体的值用于绑定原始函数的参数,也可以是占位符std::placeholders::_n
,占位符表示新函数的参数位置。
代码案例 :使用
std::bind
绑定器生成一个新的可调用对象
cpp
#include <iostream>
#include <functional>
using namespace std;
//实现:三数相乘的"原始函数"
int multiply(int a, int b, int c)
{
return a * b * c;
}
int main()
{
// ==================== 使用 std::bind 绑定参数 ====================
auto newFunc = bind(multiply, 2, placeholders::_1, placeholders::_2);
/* bind 函数的作用:创建一个新的可调用对象,固定原函数的某些参数
* 语法:std::bind(原函数, 参数1, 参数2, ...)
* 这里:
* 1. 将 multiply 的第一个参数固定为 2
* 2. 使用占位符 _1 和 _2 表示新函数的第1和第2个参数
* 3. 占位符来自命名空间 std::placeholders,因 using 语句可直接使用
*/
// ==================== 调用绑定后的函数 ====================
cout << "newFunc(3, 4) = " << newFunc(3, 4) << endl; // 输出 2*3*4=24
/* 调用 newFunc(3, 4) 时:
* 3会被传递给占位符 _1,对应 multiply 的第二个参数
* 4会被传递给占位符 _2,对应 multiply 的第三个参数
* 等价于调用 multiply(2, 3, 4)
*/
return 0;
}

占位符的作用:
占位符
std::placeholders::_n
:表示新函数的参数位置。(n
表示参数位置)
placeholders::_1
,placeholders::_2
, ...:是std::placeholders
命名空间中的对象
placeholders::_1
,placeholders::_2
, ... :用于指定新函数的参数如何映射到原函数的参数位置
- 例如 :
_1
表示新函数的第一个参数,在调用时会被传递给原函数对应的位置
新函数的调用逻辑:
cppnewFunc(3, 4);
- 绑定时:
a=2
(固定值),b
和c
由占位符指定- 调用时:
3
和4
分别填充到占位符位置,最终执行2*3*4
调用
newFunc(3, 4);
时,参数传递路径为:
cpp3 → _1 → multiply 的第二个参数 4 → _2 → multiply 的第三个参数
调用
newFunc(3, 4);
等价于调用multiply(2, 3, 4)
std::bind扩展说明:
多参数绑定
cpp// 固定 multiply 的第二个参数为 3 auto func = bind(multiply, placeholders::_1, 3, placeholders::_2); func(2, 4); // 等价于 multiply(2, 3, 4)
参数重排序
cpp// 交换原函数的第二个和第三个参数 auto swapped = bind(multiply, placeholders::_1, placeholders::_3, placeholders::_2); //注:swapped是bind包装multiply后返回的新可调用对象 swapped(2, 3, 4); // 等价于 multiply(2, 4, 3)
绑定成员函数
cpp#include <iostream> #include <functional> using namespace std; //定义一个包含成员函数的类 struct Calculator { //实现:计算两个整数的和的"成员函数" int add(int a, int b) { return a + b; } }; int main() { //1.创建 Calculator 类的实例 Calculator calc; // ==================== 使用 std::bind 绑定成员函数 ==================== //2.绑定成员函数 auto boundAdd = bind(&Calculator::add, &calc, placeholders::_1, placeholders::_2); /* 绑定语法: * std::bind(成员函数指针, 对象指针, 参数1, 参数2, ...) * * 这里: * 1. &Calculator::add:成员函数的地址(必须使用 & 取地址) * 2. &calc:对象的指针(绑定到哪个对象实例) * 3. placeholders::_1, placeholders::_2:新函数的第1和第2个参数 */ // ==================== 调用绑定后的函数对象 ==================== //3.调用绑定后的函数对象 cout << "boundAdd(3, 4) = " << boundAdd(3, 4) << endl; // 输出 7 /* 调用 boundAdd(3, 4) 时: * 3 和 4 分别填充到占位符 _1 和 _2 的位置 * 等价于调用 calc.add(3, 4) */ return 0; }

成员函数绑定的特殊要求:
cppbind(&Calculator::add, &calc, ...)
- 必须使用&取成员函数地址:
&Calculator::add
是正确写法- 必须传递对象指针:
&calc
指定成员函数要作用于哪个对象实例- 若 Calculator::add 是 const 成员函数,则需传递 const Calculator*
绑定成员函数与普通函数的区别:
特性 | 普通函数绑定 | 成员函数绑定 |
---|---|---|
第一个参数 | 函数名(自动转换为函数指针) | 必须显式使用 &类名::成员函数名 |
是否需要对象指针 | 否 | 必须(指定调用该成员函数的对象) |
示例 | bind(func, _1, _2) |
bind(&Class::method, &obj, _1) |
代码示例:绑定器 bind 的使用大总结
cpp
#include <iostream>
#include <functional>
#include <utility> // 用于支持 move 语义
using namespace std;
// 引入占位符 _1、_2、_3,简化 bind 中参数占位的写法
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
//1.实现:两参数减法运算,结果放大 10 倍
int Sub(int a, int b)
{
return (a - b) * 10;
}
//2.实现:三参数减法运算,结果放大 10 倍
int SubX(int a, int b, int c)
{
return (a - b - c) * 10;
}
//3.类 Plus,包含"静态成员函数"和"普通成员函数"
class Plus
{
public:
//3.1:静态成员函数:两整数的加法(静态函数属于类,无需对象即可调用)
static int plusi(int a, int b)
{
return a + b;
}
//3.2:普通成员函数:两浮点数的加法(需要通过对象调用,隐含 this 指针)
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
// ========== 1. 基础用法:绑定函数参数,保持参数顺序 ==========
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl; //等价于直接调用 Sub(10, 5),计算 (10 - 5) * 10 = 50
// ========== 2. 调整参数顺序(展示 bind 灵活调整参数的能力) ==========
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl; //等价于 Sub(5, 10),计算 (5 - 10) * 10 = -50
// ========== 3. 固定部分参数(常用场景:简化调用,减少重复传参) ==========
//1. 固定 Sub 的第一个参数为 100
auto sub3 = bind(Sub, 100, _1);
cout << sub3(5) << endl; //等价于 Sub(100, 5),计算 (100 - 5) * 10 = 950
//2. 固定 Sub 的第二个参数为 100
auto sub4 = bind(Sub, _1, 100);
cout << sub4(5) << endl; //等价于 Sub(5, 100),计算 (5 - 100) * 10 = -950
// ========== 4. 绑定类成员函数(区分静态和普通成员函数) ==========
// 方式 1:使用"对象"绑定类Plus的"普通成员函数"plusd
//1.创建类Plus的对象实例
Plus pd;
//2.使用"对象"绑定类Plus的"普通成员函数"
function<double(Plus, double, double)> f6 = &Plus::plusd; //绑定"普通成员函数",需传入对象实例才能调用(因为依赖 this 指针)
//3.通过"对象"调用
cout << f6(pd, 1.1, 1.1) << endl; //通过 pd 对象调用 plusd(1.1, 1.1),结果 1.1 + 1.1 = 2.2
//4.通过"临时对象"调用
cout << f6(move(Plus()), 1.1, 1.1) << endl; //用临时对象调用,move 优化(也可直接传 Plus(),效果一样,这里展示 move 用法)
// 方式 2:使用 bind 绑定成员函数plusd + 固定对象实例Plus()
function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2); //绑定到临时 Plus 对象,调用时传 _1、_2 对应 plusd 的两个参数
cout << f7(1.1, 1.1) << endl; // 等价于 Plus().plusd(1.1, 1.1),结果 2.2
return 0;
}

类成员函数绑定(f6、f7)
- 绑定普通成员函数时,必须传入对象实例(pd 或临时对象 Plus()),因为成员函数隐含 this 指针
- 用 bind 可以 "固定对象实例",让后续调用更简洁(如 :f7 直接传两个 double 即可)
3. 包装器怎么应用?
3.1:std::function的应用
1. 可调用对象的 "映射表"
- 用
std::map
建立 "字符串 → 可调用对象" 的映射,通过std::function
统一类型。std::function 的作用:统一包装不同的 lambda 表达式(比如:加法、减法等)等,使它们能存储在 map 中,实现 "运算符 → 逻辑" 的映射。
cpp
#include <iostream>
#include <functional>
#include <map>
#include <string>
using namespace std;
int main()
{
//1.定义一个映射表(map),用于存储"名称→函数逻辑"的对应关系
/* 说明:
* 1. 键(key)类型:string(函数的名称标识)
* 2. 值(value)类型:function<int(int)>(可调用对象包装器,接受int参数并返回int)
*/
map<string, function<int(int)>> funcMap =
{
//1.1:向映射表中添加第一个键值对:
{"double", [](int x) { return x * 2; }}, //键为"double",值为一个lambda表达式(匿名函数)
//1.2:向映射表中添加第二个键值对:
{"addFive", [](int x) { return x + 5; }}, //键为"addFive",值为另一个lambda表达式
};
//2.调用映射表中的函数逻辑:
//2.1:通过键"double"从映射表中获取对应的函数,传入参数3并执行
auto result1 = funcMap["double"](3);
cout << "调用 double(3) 的结果: " << result1 << endl;
//2.2:通过键"addFive"从映射表中获取对应的函数,传入参数3并执行
auto result2 = funcMap["addFive"](3);
cout << "调用 addFive(3) 的结果: " << result2 << endl;
return 0;
}

实践案例1 :150. 逆波兰表达式求值

***方法一:***方传统方式的实现
cpp
/*-------------------------- 传统方式的实现 --------------------------*/
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
//1.定义一个栈,用于存储操作数
stack<int> stk;
//2.遍历 tokens 中的每个元素
for (auto& str : tokens)
{
//2.1:如果当前遍历到的字符是"运算符"
if (str == "+" || str == "-" || str == "*" || str == "/")
{
//第一步:先栈顶取出第二个操作数(后入栈的操作数,即右操作数)
int right = stk.top();
stk.pop();
//第二步;再栈顶取出第一个操作数(先入栈的操作数,即左操作数)
int left = stk.top();
stk.pop();
//第三步:根据运算符类型,执行对应的计算
switch (str[0])
{
case '+':
stk.push(left + right);
break;
case '-':
stk.push(left - right);
break;
case '*':
stk.push(left * right);
break;
case '/':
stk.push(left / right);
break;
}
}
//2.2:如果当前遍历到的字符是"操作数"
else
{
//1.将其转换为 int 后入栈
stk.push(stoi(str)); // stoi(str):将字符串形式的操作数转换为 int 类型,方便入栈计算
}
}
//3.栈顶元素即为最终结果
return stk.top();
}
};

***方法二:***map映射实现
cpp
/*-------------------------- 使用 map 映射 string 和 function 的方式实现 --------------------------*/
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
//1.定义一个栈,用于存储操作数
stack<int> stk;
//2.定义一个 map,用于映射运算符和对应的计算函数
/* 说明:
* 1. key: 运算符(如 "+"、"-"、"*"、"/")
* 2. value: function<int(int, int)> 类型的可调用对象,接收两个 int 参数,返回一个 int 结果
*/
map<string, function<int(int, int)>> opFuncMap =
{
//2.1:加法运算
{"+", [](int x, int y) { return x + y; }},
//2.2:减法运算
{"-", [](int x, int y) { return x - y; }},
//2.3:乘法运算
{"*", [](int x, int y) { return x * y; }},
//2.4:除法运算
{"/", [](int x, int y) { return x / y; }}
};
//3.遍历 tokens 中的每个元素
for (auto& str : tokens)
{
//3.1:如果当前遍历到的字符是"运算符"
if (opFuncMap.count(str))
{
//第一步:先栈顶取出第二个操作数(后入栈的操作数,即右操作数)
int right = stk.top();
stk.pop();
//第二步;后栈顶取出第一个操作数(先入栈的操作数,即左操作数)
int left = stk.top();
stk.pop();
//第三步:根据运算符,调用对应的计算函数,并将结果入栈
int ret = opFuncMap[str](left, right);
stk.push(ret);
}
//3.2:如果当前遍历到的字符是"操作数"
else
{
//1.将其转换为 int 后入栈
stk.push(stoi(str)); // stoi(str):将字符串形式的操作数转换为 int 类型,方便入栈计算
}
}
//4.栈顶元素即为最终结果
return stk.top();
}
};

传统方式实现 VS map映射实现:
传统方式实现:
- 核心逻辑:
使用stack
存储操作数,遍历tokens
时,遇到运算符则从栈中弹出两个操作数,根据运算符类型(switch-case
判断)执行计算,结果重新入栈;遇到操作数则转换为int
后入栈。- 优缺点:
- 优点:
逻辑直观
,适合简单的四则运算场景- 缺点:新增运算符时,需要修改 switch-case 分支,
扩展性较差
map映射实现:
- 核心逻辑:
利用std::map
建立运算符 → 计算函数 的映射关系,遍历tokens
时,遇到运算符则从map
中获取对应的计算函数,执行运算;遇到操作数则转换为int
后入栈。- 优缺点:
- 优点:
扩展性强
,新增运算符时只需在map
中添加映射关系,无需修改核心逻辑;代码更简洁
,利用 std::function 和 lambda 表达式简化函数调用- 缺点:
map 的查找效率为 O(log n)
(n
为运算符数量),理论上略低于 switch-case 的 O ( 1 ) O(1) O(1),但实际场景中差异可忽略
2. 与 lambda/仿函数的配合
- 因为
std::function
能包裹lambda
,所以可简化代码中"函数对象类型不明确" 的问题
cpp
#include <iostream>
#include <functional>
using namespace std;
int main()
{
//1.将lambda表达式赋值给std::function对象
function<void()> printHello = []
{
cout << "Hello, std::function!" << endl;
};
/* 说明:
* 1. function<void()> 表示:包装一个无参数、无返回值的可调用对象
* 2. lambda表达式 []{} :无捕获列表,无参数,函数体为输出一句话
*/
//2.调用function对象,本质是执行包装的lambda表达式
printHello();
return 0;
}

综上:
std::function 是 C++ 中管理可调用对象的 "胶水层",通过统一类型包装,让函数指针、lambda 等能更方便地协同工作,尤其在复杂场景(如:回调管理、策略模式 )中优势显著。
3.2:std::bind的应用
1. 与 lambda/仿函数的配合
cpp
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders; //引入占位符命名空间,让 _1、_2 等直接可用
int main()
{
// ========== 结合 bind + lambda,实现复杂逻辑(复利计算场景) ==========
//1.定义lanbda表达式:计算复利利息
auto func1 = [](double rate, double money, int year) -> double
{
//1.定义变量记录总金额
double ret = money;
//2.复利公式:每年利息加入本金,循环 year 次
for (int i = 0; i < year; ++i)
{
ret += ret * rate;
}
//3.返回总利息(最终金额 - 本金)
return ret - money;
};
//2.固定利率、年数,只让本金作为可变参数,生成专用函数
// 场景 1:利率 3.5%,3 年,不同本金 → 调用时只需传本金
function<double(double)> func3_3_5 = bind(func1, 0.035, _1, 3);
// 场景 2:利率 1.5%,5 年
function<double(double)> func5_1_5 = bind(func1, 0.015, _1, 5);
// 场景 3:利率 2.5%,10 年
function<double(double)> func10_2_5 = bind(func1, 0.025, _1, 10);
// 场景 4:利率 3.5%,30 年
function<double(double)> func30_3_5 = bind(func1, 0.035, _1, 30);
//3.测试:本金 1000000,计算各场景利息
cout << "场景 1(3.5%利率,3年)利息:" << func3_3_5(1000000) << endl;
cout << "场景 2(1.5%利率,5年)利息:" << func5_1_5(1000000) << endl;
cout << "场景 3(2.5%利率,10年)利息:" << func10_2_5(1000000) << endl;
cout << "场景 4(3.5%利率,30年)利息:" << func30_3_5(1000000) << endl;
return 0;
}

std::bind 的价值总结:
- 解耦参数传递:无需修改原始函数,就能调整参数的传递逻辑。
- 适配复杂调用:在回调函数、策略模式中,灵活调整参数,让接口更通用。
- 简化重复调用:固定常用参数,减少重复传参的冗余代码。
简单说 :
std::bind
是 C++ 中灵活处理 "可调用对象参数" 的工具,通过包装和适配,让函数调用更灵活、更贴合复杂场景~
