【C++】C++11 包装器function 与 绑定器 bind

【C++】C++11 包装器function 与 绑定器 bind 目录一、 std::function可调用对象的类型擦除与统一包装1. 为什么需要 std::function2. std::function 语法与应用解析二、 std::bind参数绑定与调用适配1. std::bind 的核心机制2. std::bind 的语法与占位符三、 利用 bind 实现参数柯里化四、 核心总结在 C11 中可调用对象Callable Object包括函数指针、仿函数重载了operator()的类对象、Lambda 表达式以及类的成员函数 。这些类型各异的可调用对象在配合模板使用时会引发模板实例化膨胀问题 。为了统一和管理这些不同类型的可调用对象C11 引入了functional头文件中的std::function和std::bind。一、 std::function可调用对象的类型擦除与统一包装1. 为什么需要 std::function当我们将不同的可调用对象作为参数传递给函数模板时编译器会为每一种实际类型实例化出一份独立的代码 。templateclassF,classTTuseF(F f,T x){staticintcount0;coutcount:countendl;coutcount:countendl;returnf(x);}如果在实际调用中普通函数、仿函数对象和Lambda 表达式编译器会实例化出三份不同的useF函数 。这种行为会导致代码膨胀降低编译效率。std::function本质是一个类模板包装器/适配器 。它通过类型擦除技术能够在统一的接口下存储和调用任何具有相同调用签名的可调用对象。使用std::function接收参数可以强制所有兼容的可调用对象转换为同一种类型从而避免模板被多次实例化。2. std::function 语法与应用解析std::function的模板参数是函数的签名std::function返回值类型(参数类型列表)。#includeiostream#includefunctionalusingnamespacestd;// 1. 普通函数intf(inta,intb){returnab;}// 2. 仿函数structFunctor{intoperator()(inta,intb){returnab;}};// 3. 类包含静态与非静态成员函数classPlus{public:staticintplusi(inta,intb){returnab;}doubleplusd(doublea,doubleb){returnab;}};intmain(){// 包装普通可调用对象函数指针、仿函数、Lambdafunctionint(int,int)f1f;functionint(int,int)f2Functor();functionint(int,int)f3[](inta,intb){returnab;};coutf1(1,1)endl;coutf2(1,1)endl;coutf3(1,1)endl;// 包装静态成员函数// 静态成员函数属于类不依赖对象因此只需指定类域和取地址functionint(int,int)f4Plus::plusi;coutf4(1,1)endl;// 包装非静态成员函数// 深入解析非静态成员函数的底层第一个参数是隐藏的 this 指针。// 因此在 function 的参数列表中必须显式地增加一个类对象或类指针类型的参数。Plus pd;// 方式一传入对象指针Plus*functiondouble(Plus*,double,double)f5Plus::plusd;coutf5(pd,1.1,1.1)endl;// 调用时传入对象的地址// 方式二传入对象Plus底层通常是通过对象调用成员函数functiondouble(Plus,double,double)f6Plus::plusd;coutf6(pd,1.1,1.1)endl;// 传入具名对象coutf6(Plus(),1.1,1.1)endl;// 传入匿名对象return0;}关键点说明包装类的非静态成员函数时必须使用类名::函数名的严格语法取地址。同时由于非静态成员函数依赖this指针其实际参数列表比显式参数多一个对象指针/对象参数这一点在定义function类型时必须体现。二、 std::bind参数绑定与调用适配1. std::bind 的核心机制std::bind是一个函数模板它接收一个可调用对象并生成一个新的可调用对象 。它的核心作用是对现有函数的参数进行重新映射、固定或调整顺序。从底层来看std::bind返回的是一个未指定类型的仿函数对象。当我们调用这个生成的仿函数时它会将我们传入的参数与内部绑定的参数进行整合最终去调用原始的可调用对象。2. std::bind 的语法与占位符std::bind使用std::placeholders命名空间中的占位符如_1,_2来表示新生成函数的参数位置 。_1代表新函数的第一个参数_2代表第二个以此类推 。#includeiostream#includefunctionalusingnamespacestd;usingplaceholders::_1;usingplaceholders::_2;usingplaceholders::_3;intSub(inta,intb){return(a-b)*10;}intSubX(inta,intb,intc){return(a-b-c)*10;}classPlus{public:doubleplusd(doublea,doubleb){returnab;}};intmain(){// 1. 调整参数顺序// _2 代表 sub2 被调用时的第二个参数_1 代表第一个参数// 当执行 sub2(10, 5) 时参数映射为 Sub(5, 10)autosub2bind(Sub,_2,_1);coutsub2(10,5)endl;// 2. 调整参数个数固定参数/偏函数应用// 绑死第一个参数为 100生成一个只需要一个参数的新函数autosub3bind(Sub,100,_1);coutsub3(5)endl;// 映射为 Sub(100, 5)autosub4bind(Sub,_1,100);coutsub4(5)endl;// 映射为 Sub(5, 100)// 对于多参函数可以任意绑死某几个参数autosub5bind(SubX,100,_1,_2);coutsub5(5,1)endl;// SubX(100, 5, 1)autosub6bind(SubX,_1,100,_2);coutsub6(5,1)endl;// SubX(5, 100, 1)autosub7bind(SubX,_1,_2,100);coutsub7(5,1)endl;// SubX(5, 1, 100)// 3. 配合 std::function 绑死类成员函数的 this 指针// 将对象 Plus() 作为隐藏的 this 指针绑死在第一个参数位置// 这样成员函数就可以像普通函数一样被包装为只需要两个 double 参数的 functionfunctiondouble(double,double)f7bind(Plus::plusd,Plus(),_1,_2);coutf7(1.1,1.1)endl;return0;}关键点说明bind绑定类对象时如Plus()通常是值拷贝。如果需要引用绑定需要使用std::ref或直接传递对象的指针。使用bind将成员函数的this参数固定后极大地简化了其在回调函数、事件机制中的调用接口。三、 利用 bind 实现参数柯里化柯里化:将多参数函数转换为一系列单参数函数的技术使得函数可以逐步接收参数并支持参数复用和函数组合。std::bind在工程实践中常用于降低函数的调用维度。例如将一个通用的计算函数通过绑定固定参数特化为特定业务场景的具体函数。#includeiostream#includefunctionalusingnamespacestd;usingplaceholders::_1;intmain(){// 基础计算模型计算指定利率、本金、年限下的复利净利润autofunc1[](doublerate,doublemonty,intyear)-double{doubleretmonty;for(inti0;iyear;i){retret*rate;}returnret-monty;};// 业务场景特化将基础计算模型包装为单参数的特定理财产品计算器// 绑死利率(参数1)和年限(参数3)只保留本金(参数2)作为占位符 _1 接收输入functiondouble(double)func3_1_5bind(func1,0.015,_1,3);// 1.5%利率3年期functiondouble(double)func5_1_5bind(func1,0.015,_1,5);// 1.5%利率5年期functiondouble(double)func10_2_5bind(func1,0.025,_1,10);// 2.5%利率10年期functiondouble(double)func20_3_5bind(func1,0.035,_1,30);// 3.5%利率30年期// 调用特化后的函数逻辑更加清晰接口更加简洁coutfunc3_1_5(1000000)endl;coutfunc5_1_5(1000000)endl;coutfunc10_2_5(1000000)endl;coutfunc20_3_5(1000000)endl;return0;}通过这种方式底层复杂的通用接口被解耦并转化为更高层、更具体的业务接口这也是函数式编程思维在 C 中的具体体现。四、 核心总结std::function的核心在于类型统一解决了模板函数因接收不同可调用类型而导致的实例化膨胀问题 。非静态成员函数在被包装时由于包含隐藏的this指针必须显式传递对象或指针参数。std::bind的核心在于接口适配通过内部生成仿函数并映射参数位置可以调整函数参数的顺序、固定部分参数或是抹平类成员函数的对象依赖 。返回的未命名类型通常使用auto接收或转换并赋值给签名匹配的std::function对象。