当前位置: 首页 > news >正文

《C++进阶之C++11》【lambda表达式 + 包装器】

【lambda表达式 + 包装器】目录

  • 前言:
  • ------------ lambda表达式 ------------
    • 1. 什么是lambda表达式?
    • 2. lambda表达式的基本语法形式?
      • 2.1:捕获列表
      • 2.2:参数列表与返回值
    • 3. lambda表达式怎么使用?
    • 4. 特殊的捕获限制有哪些?
    • 5. lambda表达式的价值是什么?
    • 6. lambda表达式的应用有哪些?
    • 7. lambda表达式的原理是什么?
  • ------------ 包装器 ------------
    • 1. 什么是包装器?
    • 2. 常见的包装器有哪些?
      • 2.1:函数包装器(std::function)
      • 2.2:绑定器(std::bind)
    • 3. 包装器怎么应用?
      • 3.1:std::function的应用
      • 3.2:std::bind的应用

在这里插入图片描述

往期《C++初阶》回顾:

《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模板参数接收其匿名类型


主要特点:

  1. 匿名性:没有函数名
  2. 简洁性:通常用于简单操作,可以在一行内完成
  3. 临时性:常用于一次性使用或作为参数传递给高阶函数

2. lambda表达式的基本语法形式?

基本语法形式:

[捕获列表](参数列表) -> 返回类型 
{ 函数体 
}[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的形式了:

// 空lambda(所有可省略部分均省略)
auto emptyLambda = [] {};  // 合法(但无实际逻辑)

2.1:捕获列表

lambda表达式默认仅能访问自身函数体参数列表里的变量。若需使用外层作用域(如:包含 lambda 的函数作用域)的变量,需通过 捕获列表 显式声明。


捕获列表决定了 lambda 表达式对外部变量的访问方式,常见的捕获方式有以下几种:

显示捕获:在捕获列表中明确写出变量名,用逗号分隔多个变量,同时通过符号区分捕获方式:

  • 值捕获:直接写变量名(如:[x, y]

  • 引用捕获:变量名前加 &(如:[&z]


隐式捕获:无需逐个写变量名,用符号批量指定捕获规则,编译器会自动识别 lambda 内用到的外层变量并捕获:

  • 按值隐式捕获:捕获列表写 [=] ,lambda 内用到的外层变量全部传值捕获(创建副本)
  • 按引用隐式捕获:捕获列表写 [&] ,lambda 内用到的外层变量全部传引用捕获(使用引用)

混合捕获:同时用隐式捕获符号=&)和显式变量


值捕获在 lambda 内部对捕获变量的修改不会影响外部的原始变量。

  • 例如[a]表示按值捕获变量 a
#include <iostream>
using namespace std;int main()
{//1.定义一个局部变量,初始值为10int num = 10;//2.定义一个lambda表达式,通过值捕获方式获取外部变量numauto 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;  // 输出: 10return 0;
}

在这里插入图片描述


引用捕获在 lambda 内部对捕获变量的修改会直接影响外部的原始变量。

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

在这里插入图片描述


隐式捕获可以让编译器自动推导捕获列表。 分为:

  • 按值隐式捕获[=]
  • 按引用隐式捕获[&]
#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;  // 输出: 30return 0;
}

在这里插入图片描述


混合捕获可以同时使用值捕获和引用捕获。

  • 若开头用 [=](隐式传值),后续显式变量必须传引用
    • [=, &x](其他变量隐式传值,x 显式传引用)
  • 若开头用 [&](隐式传引用),后续显式变量必须传值
    • [&, x](其他变量隐式传引用,x 显式传值)

所以[=, &a] 表示按值捕获除 a 以外的所有用到的外部变量,而 a 按引用捕获。


总结:捕获列表的常见形式

捕获列表写法含义示例
(外部变量 int a=10; double b=3.14;
[]不捕获任何外部变量lambda 内无法使用 ab
[a]传值捕获 a
(修改 lambda 内的 a 不影响外部)
lambda 内 a10 的副本
[&b]传引用捕获 b
(修改 lambda 内的 b 会影响外部)
lambda 内 b 是外部 b 的引用
[=]传值捕获所有用到的外部变量lambda 内 a 是副本,b 是副本
(修改不影响外部)
[&]传引用捕获所有用到的外部变量lambda 内 ab 都是引用
(修改会影响外部)
[a, &b]混合捕获a 传值,b 传引用)lambda 内 a 是副本,b 是引用

2.2:参数列表与返回值

参数列表:和普通函数一样,可以有不同类型的参数,也可以没有参数。

  • []() { std::cout << "Hello Lambda!" << std::endl; } 没有参数,只是输出一条信息
  • [](int a, int b) { return a + b; } 接收两个 int 类型的参数并返回它们的和

返回值

  • 如果函数体只有一条返回语句,返回值类型可以省略,编译器会自动推导。

    [](int a, int b)   //编译器会自动推导返回值类型为:int
    { return a + b; 
    }
    
  • 如果函数体有多条语句需要明确返回值类型,就必须显式指定。

    [](int a, int b) -> bool  //注意:必须显式指定返回类型:bool
    { if (a > b) return true; else return false;
    }
    

3. lambda表达式怎么使用?

代码案例1:lambda表达式的使用

#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表达式的使用

#include <iostream>
using namespace std;//定义全局变量 x
int x = 0;int main()
{// ====================== 1. “全局变量”与“空捕获列表” ======================auto func1 = []() //注意:全局变量无需捕获即可使用,lambda 捕获列表必须为空{x++;  };// ====================== 2. 显式捕获(传值 + 传引用) ======================//1.定义局部变量 a,b,c,dint a = 0, b = 1, c = 2, d = 3;//2.显式捕获:a 传值(const 只读),b 传引用(可修改)auto func2 = [a, &b](){// a++;  // 错误:传值捕获的变量默认 const,无法修改(除非加 mutable)b++;     // 正确:传引用捕获,可直接修改外部变量 bint 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=2return ret;      };cout << func3() << endl; // ====================== 4. 隐式传引用捕获([&]) ======================//4.隐式传引用捕获所有用到的变量(a, c, d)auto func4 = [&](){a++;  // 正确:传引用,修改外部 a → a=1c++;  // 正确:传引用,修改外部 c → c=3d++;  // 正确:传引用,修改外部 d → d=4};func4();cout << a << " " << b << " " << c << " " << d << endl;// ====================== 5. 混合捕获(隐式传引用 + 显式传值) ======================//5. 隐式传引用(&) + 显式传值(a, b)//规则:隐式符号(&)在前,显式变量必须传值auto func5 = [&, a, b](){// a++;  // 错误:a 是显式传值,默认 const// b++;  // 错误:b 是显式传值,默认 constc++;  // 正确:隐式传引用,修改外部 c → c=4d++;  // 正确:隐式传引用,修改外部 d → d=5return 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=2b++;  // 正确:显式传引用,修改外部 b → b=3// c++;  // 错误:c 是隐式传值,默认 const// d++;  // 错误:d 是隐式传值,默认 constreturn 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=0return 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表达式通过 简洁语法 实现了匿名函数对象,核心价值在于:

  1. 支持函数内部定义,灵活捕获局部变量
  2. 语法各部分可按需省略,适配不同场景(如:无参、无返回值等)
  3. 配合 auto 使用,无需显式定义函数类型,让代码更简洁

掌握 lambda 的语法结构和捕获规则,是高效使用 C++ 现代特性(如:算法库、异步编程)的基础。

6. lambda表达式的应用有哪些?

在学习 lambda 表达式之前,我们所使用的可调用对象只有 函数指针仿函数对象

  • 函数指针的类型定义较为繁琐
  • 仿函数需要定义一个类,操作相对麻烦

而使用 lambda 来定义可调用对象,简单又便捷。


lambda 在很多场景中也十分好用:

  • 在线程里定义线程的执行函数逻辑
  • 在智能指针中定制删除器等

lambda 的应用场景广泛,后续我们会不断接触到相关用法。


代码案例:lambda表达式应用的优越性(与传统的仿函数进行对比)

#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表达式的本质

#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” 的对比:

// 手动定义的仿函数类
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::functionstd::bind 以及智能指针(某种意义上也算资源包装器 )

包装器的核心作用:

  • 统一接口:把不同类型的可调用对象(函数指针、仿函数、lambda 等)“包装” 成相同类型,方便存储、传递。
    • 比如:lambda 是匿名类型,直接用很难统一管理,但 std::function 能把它们包装成一致的类型
  • 简化使用:隐藏底层复杂细节,调用时不用关心被包装对象的具体类型,按统一方式调用即可。
  • 扩展功能:可以在包装器里添加额外逻辑(比如:日志、异常处理 ),被包装的对象无需修改。

2. 常见的包装器有哪些?

常见的包装器有:

  • 函数包装器std::function
  • 绑定器std::bind
  • 智能指针std::unique_ptrstd::shared_ptr

2.1:函数包装器(std::function)

C++ 中通过类模板实现 std::function,核心原型简化如下:(标准库简化)

// 前向声明基础模板(未定义具体逻辑)
template <class T>  
class function;  // 特化模板:用于匹配“返回值为 Ret、参数为 Args...”的可调用对象  
template <class Ret, class... Args>  
class function<Ret(Args...)>;  

std::function:是 C++ 标准库 <functional> 里的 类模板,专门用来包装可调用对象函数指针仿函数lambdastd::bind结果等 ),使它们具有统一的调用方式,从而方便存储、传递和管理。


std::function 是可调用对象的 “包装器”,关键特性:

  1. 支持可调用对象
    能包裹函数指针、仿函数(函数对象)、lambda 表达式、std::bind 绑定的表达式等
  2. “空” 状态与异常
    std::function 未绑定任何可调用对象(空状态 ),调用时会抛出 std::bad_function_call 异常
  3. 统一类型抽象
    无论原始可调用对象类型多复杂(:lambda 是匿名类型 ),都能被 std::function 包装为相同类型,便于声明和使用

std::function的类型声明:

function<int(int, int)>
  • 这里 std::function<int(int, int)> 就是 “包装器类型”
  • 模板参数 <int(int, int)> 表示:返回值为 int,接受两个 int 参数的可调用对象
  • 可简化理解为:function<返回类型(参数类型列表)>

意义:不管底层是函数指针、仿函数还是 lambda,都被包装成相同类型,调用方式完全一致。


代码示例1:用 std::function 统一包装不同可调用对象

#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函数

// ==================== 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;};

不同可调用对象的统一调用语法:

func1(3, 4);  // 无论底层是函数、仿函数还是 lambda,调用方式完全一致

意义:体现了 std::function 的核心价值:类型擦除,统一接口

  • std::function 内部通过类型擦除技术,封装了不同类型的可调用对象,对外提供统一的调用接口
  • std::function 存在一定的性能开销(虚函数调用),对于性能敏感场景,可考虑使用函数指针或模板

代码示例2:用 std::function 包装“静态成员函数”和“普通成员函数”

#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 转为右值,调用临时对象的 Funccout << f7(move(pd), 1.1, 1.1) << endl;//3.2:直接构造临时对象 Plus(),调用其 Funccout << f7(Plus(), 1.1, 1.1) << endl;return 0;
}

在这里插入图片描述

2.2:绑定器(std::bind)

std::bind有两种典型声明形式:(标准库简化)

// 形式 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. 调整参数顺序:重新排列可调用对象的参数顺序。
  3. 参数占位:用占位符(_1_2 )预留参数位置,让新的可调用对象能接收动态参数。

std::bind的类型声明:

// 调用格式:
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 绑定器生成一个新的可调用对象

#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 表示新函数的第一个参数,在调用时会被传递给原函数对应的位置

新函数的调用逻辑:

newFunc(3, 4);
  • 绑定时a=2(固定值),bc 由占位符指定
  • 调用时34 分别填充到占位符位置,最终执行 2*3*4

调用 newFunc(3, 4);时,参数传递路径为:

3 → _1 → multiply 的第二个参数
4 → _2 → multiply 的第三个参数

调用newFunc(3, 4);等价于调用 multiply(2, 3, 4)


std::bind扩展说明:

  • 多参数绑定

    // 固定 multiply 的第二个参数为 3
    auto func = bind(multiply, placeholders::_1, 3, placeholders::_2);func(2, 4);  // 等价于 multiply(2, 3, 4)
    

  • 参数重排序

    // 交换原函数的第二个和第三个参数
    auto swapped = bind(multiply, placeholders::_1, placeholders::_3, placeholders::_2); //注:swapped是bind包装multiply后返回的新可调用对象swapped(2, 3, 4);  // 等价于 multiply(2, 4, 3)
    

  • 绑定成员函数

    #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;
    }
    

在这里插入图片描述

成员函数绑定的特殊要求:

bind(&Calculator::add, &calc, ...)
  • 必须使用&取成员函数地址&Calculator::add 是正确写法
  • 必须传递对象指针&calc 指定成员函数要作用于哪个对象实例
  • 若 Calculator::add 是 const 成员函数,则需传递 const Calculator*

绑定成员函数与普通函数的区别:

特性普通函数绑定成员函数绑定
第一个参数函数名(自动转换为函数指针)必须显式使用 &类名::成员函数名
是否需要对象指针必须(指定调用该成员函数的对象)
示例bind(func, _1, _2)bind(&Class::method, &obj, _1)

代码示例:绑定器 bind 的使用大总结

#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 的第一个参数为 100auto sub3 = bind(Sub, 100, _1);cout << sub3(5) << endl;  //等价于 Sub(100, 5),计算 (100 - 5) * 10 = 950//2. 固定 Sub 的第二个参数为 100auto 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.2return 0;
}

在这里插入图片描述

类成员函数绑定(f6、f7)

  • 绑定普通成员函数时,必须传入对象实例(pd 或临时对象 Plus()),因为成员函数隐含 this 指针
  • 用 bind 可以 “固定对象实例”,让后续调用更简洁( :f7 直接传两个 double 即可)

3. 包装器怎么应用?

3.1:std::function的应用

1. 可调用对象的 “映射表”

  • std::map 建立 “字符串 → 可调用对象” 的映射,通过 std::function 统一类型。

std::function 的作用统一包装不同的 lambda 表达式(比如:加法、减法等)等,使它们能存储在 map 中,实现 “运算符 → 逻辑” 的映射。

#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;
}

在这里插入图片描述

实践案例1150. 逆波兰表达式求值

在这里插入图片描述

方法一:方传统方式的实现

/*-------------------------- 传统方式的实现 --------------------------*/
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映射实现

/*-------------------------- 使用 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,所以可简化代码中 “函数对象类型不明确” 的问题
#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/仿函数的配合

#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++ 中灵活处理 “可调用对象参数” 的工具,通过包装和适配,让函数调用更灵活、更贴合复杂场景~

在这里插入图片描述

http://www.dtcms.com/a/430564.html

相关文章:

  • 【C++】栈、队列、双端队列、优先级队列、仿函数
  • 潢川手机网站建设做网站的图片=gif
  • Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频语义理解与智能检索进阶
  • 图片转视频
  • AI 智能体在 2025 年面临的挑战
  • 做一元夺宝网站需要什么条件网页网站建设软件
  • 网站建设与维护的实训总结wordpress 自定义注册
  • 什么是RDMA?—— 一场网络通信的范式革命
  • 一篇文章入门RabbitMQ:基本概念与Java使用
  • @ResponseStatus 注解详解
  • Linux--权限
  • 【连载3】MySQL 的 MVCC 机制剖析
  • C++封装和继承特性
  • Linux(操作系统)文件系统--对打开文件的管理
  • 【Unity笔记】Unity XR 模式下 Point Light 不生效的原因与解决方法
  • 图片设计网站推荐wordpress下载的主题怎么安装
  • 分布式存储分片核心:从哈希取模到Redis哈希槽,从哈希类到非哈希类
  • C++ 操作 Redis
  • 旅游网站开发文献综述沈阳做网站大约要多少钱
  • 精美个人网站wordpress设置网站主题
  • PyCharm保姆级详细使用手册(Python新手快速上手篇)
  • 3.springboot-容器功能-@注解
  • python开发手机网站开发今天时政新闻热点是什么
  • 【网络编程】深入 HTTP:从报文交互到服务构建,洞悉核心机制
  • java面试0119-java中创建对象的方式?
  • 线程中互斥锁和读写锁相关区别应用示例
  • 网站开发logo绍兴网页设计
  • 2017主流网站风格win7 iis配置网站 视频教程
  • wordpress同步微信公众号seo外包是什么
  • 如何评价一个网站做的好不好展厅网站