线程池模拟实现的后续改进想法1问题:原生普通函数不能直接通过AddTask()对函数传参2解决方法1成员函数2lambda 捕获参数1:[var] 显式值捕获拷贝2:[var] 显式引用捕获别名3:混合显式捕获4:隐式捕获批量捕获所有变量5:隐式 单独指定6:初始化捕获C14 通用捕获7:特殊捕获规则3:std::bind 绑定参数1:bind默认拷贝2:bind的引用拷贝3:bind 绑定右值 / 移动语义std::move4:占位符 std::placeholders::_11.占位符基础含义2:关键区分别搞混「原函数形参顺序」和「占位符顺序」3:搭配类成员函数 bind 常用场景4:让接口原生支持传参(可变模板重载)1:拆分语法:2:std::forwardArgs(args)完美转发3:包展开语法 ...arg std::forwardArgs(args)4:区分三种不同位置的 ...5:简单总结1问题:原生普通函数不能直接通过AddTask()对函数传参templateclassFuncvoidAddTask(Funcfunc){PushTask(std::forwardFunc(func));}原理说明PushTask最终把传入的可调用对象存入queueTTaskTTask强制是std::functionvoid()。不管是普通函数、成员函数只要原生带参数都必须外层包一层无参包装器bind/lambda把参数固化进去才能符合队列存储要求。内部存入队列的类型是TTask std::functionvoid()要求无参、无返回的可调用对象。普通函数void test(int a)本身签名是void(int)和void()不匹配直接传会编译报错函数的参数无法通过AddTask接口追加接口只接收一个可调用对象没有参数占位。2解决方法1成员函数成员函数专用重载专门处理类成员 对象 参数将this指针bind给普通函数方便函数后续访问类内成员。templateclassClassType,classFuncvoidAddTask(ClassType*obj,Funcfunc){PushTask(std::bind(std::forwardFunc(func),obj));}2lambda 捕获参数[捕获规则] (参数) - 返回值 { 函数体 };[]决定 lambda 能访问外部变量分为值捕获、引用捕获、隐式捕获、this 捕获、初始化捕获五大类。voidPrintMsg(conststd::stringmsg,intnum){std::coutmsg numstd::endl;}std::string stest;intn123;// lambda捕获变量封装成无参任务pool.AddTask([s,n](){PrintMsg(s,n);});lambda 捕获语法详解捕获写法含义[]不捕获任何局部变量[x]按值捕获变量x[x]按引用捕获变量x[]按值捕获所有使用到的局部变量[]按引用捕获所有使用到的局部变量[, x]默认按值捕获但x按引用捕获[, x]默认按引用捕获但x按值捕获[this]捕获当前对象的指针this[*this]拷贝整个当前对象C17[a expr]初始化捕获C14用expr初始化a1:[var] 显式值捕获拷贝把外部变量复制一份到 lambda内部修改不影响外部原变量捕获的变量默认const不可修改要改需加mutable不是变量本身 const而是 lambda 默认operator() constconst 成员函数无法修改非 mutable 成员加 mutable 移除调用运算符的 const 限定不是修改捕获变量的 const 属性。inta10;autof[a](){a20;// 报错const拷贝couta;};f();// mutable 允许修改拷贝副本autof2[a]()mutable{a20;// 只改副本外部a不变couta;};f2();couta;// 输出102:[var] 显式引用捕获别名直接绑定外部变量的地址内部修改同步影响外部生命周期风险若 lambda 生命周期长于外部变量会悬垂引用inta10;autof[a](){a20;};f();couta;// 输出203:混合显式捕获同时捕获值、引用逗号分隔intx1,y2;autof[x,y](){// x只读y可修改yx10;};4:隐式捕获批量捕获所有变量[]全部值捕获,[]全部引用捕获inta1,b2;autof[](){coutab;};intc1;autof[](){c100;};5:隐式 单独指定批量默认捕获个别变量反向控制[, x]默认全部值捕获唯独 x 用引用[, x]默认全部引用捕获唯独 x 用值拷贝禁止冲突写法[, x]、[, x]重复指定报错。inta1,b2,c3;// a、c值捕获b引用捕获autof[,b](){bac;};6:初始化捕获C14 通用捕获语法[变量名 表达式]作用捕获临时变量、右值、移动对象重命名外部变量捕获无法拷贝只能移动的类型unique_ptrinta10;// 1. 重命名捕获autof1[ba](){coutb;};// 2. 移动捕获std::moveautopstd::make_uniqueint(5);autof2[pstd::move(p)](){cout*p;};// 原p已空所有权转移到lambda内部// 3. 捕获临时表达式autof3[xa*2](){coutx;};[x ...]引用初始化捕获绑定表达式左值。含义不要求外部有变量 x直接把右边表达式...的结果用左值引用绑定到捕获变量x7:特殊捕获规则[ ]空捕获不捕获任何外部变量能使用 lambda 自身参数、函数内局部变量静态局部变量。intg100;autof[](){coutg;};3:std::bind 绑定参数// 普通带参函数voidPrintMsg(conststd::stringmsg,intnum){std::coutmsg numstd::endl;}// 使用线程池ThreadPool pool;pool.Start();// bind 把参数提前打包成无参functionpool.AddTask(std::bind(PrintMsg,hello,666));1:bind默认拷贝bind 会拷贝 / 移动传入的参数存入内部存储不是单纯 “拷贝”传入右值会移动构造副本传入左值拷贝voidfunc(intx){x100;}intmain(){inta10;autotaskstd::bind(func,a);// bind内部拷贝了一份a存的是副本不是对外部a的引用task();std::coutastd::endl;// 输出10外部a没变副本内部变成110return0;}2:bind的引用拷贝想要绑定引用必须用std::ref()左值引用/std::cref()const 左值引用std::ref/std::cref会生成一个引用包装器std::reference_wrapperTbind 识别该类型后会存储引用而不是拷贝。voidfunc(intx){x100;}intmain(){inta10;autotaskstd::bind(func,std::ref(a));task();std::coutastd::endl;// 输出110外部变量被修改return0;}3:bind 绑定右值 / 移动语义std::movebind可以接收右值但 bind 内部会永久保存为左值想要转移资源需要手动包std::movevoidfunc(std::string s){}voidfunc_rref(std::strings){}intmain(){std::string strhello;// 传入右值临时bind 允许autob1std::bind(func,std::string(temp));// 传入 move 右值bind 也允许autob2std::bind(func,std::move(str));return0;}bind 把所有传入的参数以实体对象存在内部成员里当你后续调用b1()、b2()时bind 拿内部存储的对象传给目标函数这个内部对象是有名字的成员变量 →属于左值。voidfunc_rref(std::strings){std::cout右值引用版本\\n;}voidfunc(std::string s){std::cout值拷贝版本\\n;}intmain(){std::string stest;autobstd::bind(func,std::move(s));b();// 正常运行但内部左值拷贝不移动autobstd::bind(func_rref,std::move(s));b();// 编译报错return0;}把右值存进 bind 内部可以移动构造没问题调用 bind、把内部参数传给目标函数bind 无法自动移动 / 转右值永远传左值不可拷贝类型unique_ptr无法使用 bind直接编译失败unique_ptr 只允许移动、禁止拷贝4:占位符 std::placeholders::_1bind 预留参数位后续调用传参时也可以配合 refvoidfunc(inta,intb){ba;}intmain(){intx0;autotaskstd::bind(func,std::placeholders::_1,std::ref(x));task(666);std::coutx;// 666}1.占位符基础含义_1、_2、_3… 都是占位符_1调用bind生成的可调用对象时第一个传入的参数_2调用时第二个传入参数_n第 n 个传入参数voidfunc(inta,intb){couta b;}intmain(){// 把 func 的第一个参数固定为 10第二个参数用调用时的第1个实参(_1)填充autofbind(func,10,placeholders::_1);f(20);// 调用时传入20对应 _1// 输出10 20}2:关键区分别搞混「原函数形参顺序」和「占位符顺序」占位符只和bind 生成的新函数调用时的参数挂钩和原函数参数无关。voidfunc(intx,inty){coutx,y;}intmain(){// _2 是调用 f 时的第2个参数_1 是第1个autofbind(func,placeholders::_2,placeholders::_1);f(100,200);// 调用 f 时_1100_2200// 传给 funcx_2200y_1100// 输出200,100}3:搭配类成员函数 bind 常用场景成员函数第一个隐参是this占位符依然指代调用包装对象时的参数structTest{voidtest(intv){coutv;}};intmain(){Test obj;autofbind(Test::test,obj,placeholders::_1);f(666);// _1 666输出666}4:让接口原生支持传参(可变模板重载)voidPrintMsg(conststd::stringmsg,intnum){std::coutmsg numstd::endl;}templateclassFunc,class...ArgsvoidAddTask(Funcfunc,Args...args){// 自动绑定参数生成无参任务autotask[fstd::forwardFunc(func),...argstd::forwardArgs(args)](){f(arg...);};PushTask(std::move(task));}pool.AddTask(PrintMsg,abc,789);1:拆分语法:templateclassFunc,class...ArgsvoidAddTask(Funcfunc,Args...args)Args是参数包名...代表可变可以接收 0/N 个任意类型模板参数调用时编译器自动推导所有实参类型打包进Args。Args万能引用转发引用普通T是右值引用但模板参数 组合 万能引用能同时绑定左值、右值传入左值变量Args推导为T→ArgsT 折叠成T左值引用传入临时右值Args推导为T→ArgsT右值引用Args... args参数包 函数形参包args是存储所有实参的函数形参包...写在类型后面表示把Args里每一种类型都生成一个Args形参等价于展开voidprint(int,std::string,double);AddTask(print,10,hello,3.14);// 三个参数时展开voidAddTask(Funcfunc,inta,stringb,doublec)2:std::forwardArgs(args)完美转发作用保留实参原本的左 / 右值属性不丢失移动语义左值传入 → 转发后仍是左值临时右值传入 → 转发后仍是右值支持std::move转移资源原理std::forward是带条件转换的 static_casttemplateclassTTforward(std::remove_reference_tTt)noexcept{returnstatic_castT(t);}3:包展开语法...arg std::forwardArgs(args)autotask[fstd::forwardFunc(func),...argstd::forwardArgs(args)](){f(arg...);};捕获展开...argxxx...argstd::forwardArgs(args)会对参数包里每一个元素生成捕获项调用AddTask(print,10,hi,3.14)等价展开[fforward(print),arg0forward(10),arg1forward(hi),arg2forward(3.14)]把所有函数参数转发捕获到 lambda 内部保存一份副本。调用展开f(arg...)arg...是调用时参数包展开把捕获的所有变量依次传给函数f(arg0,arg1,arg2);4:区分三种不同位置的...模板参数class... Args声明可变参数包形参Args... args声明可变参数包捕获 / 调用...arg/args...使用时展开包写法位置位置作用作用...arg xxxlambda 捕获[]包名前面批量创建一组捕获变量定义包arg...函数调用()包名后面把已有包拆成多个参数使用包class... Args模板参数声明声明可变参数包Args... args函数形参声明声明可变参数包voidfunc(std::unique_ptrintptr){}templateclassFunc,class...ArgsvoidAddTask(Funcfunc,Args...args){PushTask(std::bind(std::forwardFunc(func),std::forwardArgs(args)...));}//编译失败bind内部是左值无法绑定T右值引用templateclassFunc,class...ArgsvoidAddTask(Funcfunc,Args...args){// 自动绑定参数生成无参任务autotask[fstd::forwardFunc(func),...argstd::forwardArgs(args)](){f(arg...);};PushTask(std::move(task));}autotask[fstd::forwardFunc(func),...argstd::forwardArgs(args)](){f(arg...);};5:简单总结class... Args定义一组任意数量、任意类型的模板参数包Args... args万能引用形参包同时接收左值 / 右值std::forwardArgs(args)完美转发保留原始左 / 右值属性...argxxx/args...循环展开参数包里所有元素