news 2026/5/1 5:42:48

模板编程—模板编程处理Partial application

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
模板编程—模板编程处理Partial application

一、Partial application

Partial,大家应该比较熟悉,在模板编程中的偏特化就用这个单词,有过python编程经验的更容易理解。Partial application,大家可以把它称为“偏应用”或“部分应用”。这个名字听上去有点特别,但如果有模板偏特化的知识的就可以顺理成章的明白,可能就是把一些未知的值给定义出来,确实也是如此。
在函数编程中,将一个多参函数的部分参数固定来创建一个较少参数的新函数。大家可以理解为把其中的部分参数“重新”给个“默认值”。如果从数学的角度来看,大家可以看作是对未知参数的一个逐步“消元”的过程。给大家一个python的例程来先入为主一下,这样更容易理解:

fromfunctoolsimportpartial#set functiondeffunc(a,b,c,d):return100*(a+b+c+d)# call partial functionpf=partial(func,1,2,3)pf1=partial(func,1,2)pf2=partial(func,1)#call and printprint(pf(4))# 1000printf(pf1(3,4))#1000printf(pf2(2,3,4))#1000

上面的代码通过partial接口处理有四个参数的函数func,返回一个函数指针pf,其中三个参数已经固定为1,2,3(pf1,pf2类似),这个函数指针只需接受一个参数即可。

二、模板(元)编程实现

在上面的分析说明和python的代码展示中,应该可以很明了的知道什么是Partial application。那么就要引入到C++的模板编程中,那么在模板编程中如何实现Partial application的应用呢?在变参模板函数的应用是不是也是这样一个个的固定参数然后不断的递归处理,那么它也可以改造后应用在Partial application开发中。看下面的例子:

#include<iostream>#include<tuple>template<typename Func,typename...PartialArgs>class Partial__{private:Func f_;std::tuple<PartialArgs...>pArgs_;public:Partial__(Func func,PartialArgs...args):f_(func),pArgs_(args...){}//std::index_sequence_for是 C++14引入的一个模板别名,用于根据给定的类型参数包生成一个 std::index_sequence,其索引数量等于参数包中类型的个数。//例如:std::index_sequence_for<T1, T2, T3> 等价于std::index_sequence<0, 1, 2>template<typename...RestArgs>autooperator()(RestArgs...rArgs)const{returnpartialImpl(std::index_sequence_for<PartialArgs...>{},rArgs...);}private:template<size_t...Id,typename...RestArgs>autopartialImpl(std::index_sequence<Id...>,RestArgs...rArgs)const{returnf_(std::get<Id>(pArgs_)...,rArgs...);}};template<typename Func,typename...PartialArgs>autopartial(Func func,PartialArgs...args){returnPartial__<Func,PartialArgs...>(func,args...);}intmulSum(inta,intb,intc,intd){return10*(a+b+c+d);}intmain(){autopf2=partial(mulSum,1,2);std::cout<<"call pf2 result: "<<pf2(3,4)<<std::endl;autopf3=partial(mulSum,1,2,3);std::cout<<"call pf3 result:"<<pf3(4)<<std::endl;return0;}

也可以模仿python实现使用函数指针,C++中最常用的当然是lambda表达式:

#include<iostream>intsum(inta,intb,intc,intd){returna+b+c+d;}autocreateMul(inta){return[a](intb){returna*b;};}intmain(){//单参数处理autopSum=[](intb,intc,intd){returnsum(1,b,c,d);};std::cout<<"pSum ret: "<<pSum(2,3,4)<<std::endl;//通用autopartial=[](autofunc,auto...partialArgs){return[func,partialArgs...](auto...rArgs){returnfunc(partialArgs...,rArgs...);};};autopSum1=partial(sum,1);std::cout<<"pSum1 ret: "<<pSum1(2,3,4)<<std::endl;//return lambdastd::cout<<"return lambda ,ret:"<<createMul(2)(5)<<std::endl;return0;}

如果在C++20以上,也可以使用Lambda表达式中的模板参数包展开机制:

#include<iostream>#include<utility>template<typename F,typename...Args>autopartialImplFunc(F&&f,Args&&...allArgs){//Lambda表达式捕获列表中展开参数包return[f=std::forward<F>(f),...allArgs=std::forward<Args>(allArgs)](auto&&...rArgs)mutable{returnf(allArgs...,std::forward<decltype(rArgs)>(rArgs)...);};}intmultiply(inta,intb,intc,intd){returna*b*c*d;}intmain(){autofunc=partialImplFunc(multiply,1,2);std::cout<<func(3,4)<<std::endl;return0;}

如果在C++23的环境中则可以使用this指针和std::bind_front接口:

class multiply{public:template<typename M>autooperator()(this M&&myself,inta,intb){returna*b*myself.num_;}intnum_=1;};voidtestCpp23(){multiply demo{10};autofBind=std::bind_front(&multiply::operator(),&demo,10);std::cout<<"fBind ret : "<<fBind(10)<<std::endl;}

上面的代码在前面的文章中都有过分析,包括std::index_sequence等,对C++20中的用法也在注释中进行了说明,如果不明白,大家可参看相关的技术文档即可。
另外,在元编程的应用中,偏特化从某种角度来看也算是一种Partial application应用。另外,也可以使用std::bind函数来模拟实现功能,不过它看起来还是有些与python的实现或者说正常的实现有些不一样。

三、分析说明

Partial application应用一个特点是惰性加载或者说延迟计算,意思就是直到函数最终的调用时才会被执行。正如上面的例子,它可以不断的变换参数的数量,自由的创建函数的变体。即可以理解为创建多个有默认参数的别名同实现函数也可以认为创建多个有重载意义的不同名函数。
这种用法在普通函数编程中,确实意义没有多大,更多还是应用于模板的元编程中。

四、应用场景

新技术的应用,无非就是为了“偷懒”或解决现有技术无法方便解决的问题,Partial application出现也是如此。其应用的场景主要有:

  1. 配置参数和验证处理
    从上面的应用可以发现,在项目的一些配置选项及验证或数据库操作中的参数处理中,它有着广泛的应用可能
  2. 数据计算和数据处理
    这种应用就更加常见了,比如过滤某些特殊的参数的数据(其它都使用默认参数,只有重点的参数过滤);计算中的不同情况的计算(类似于重载)等等
  3. 算法控制
    这个和数据计算有些类似,通过不同的参数实现不同的算法策略
  4. 动态任务管理
    可以实现动态的参数配置来达到不同的任务生成,从而在设计模式、并行编程以及其它一些相应场景下应用

五、例程

下面给出一个简单的例程,用来处理不同的任务:

#include<functional>#include<iostream>#include<map>#include<string>class TaskWrap{public:using Task=std::function<void(TaskWrap&,TaskWrap&)>;voidaddTask(conststd::string&id,Task task){tasks_[id]=task;}voidrunTask(conststd::string&id,TaskWrap&tw){if(tasks_.find(id)!=tasks_.end()){tasks_[id](*this,tw);}}private:std::map<std::string,Task>tasks_;};class TaskGenerator{public:// create task1autocreateTask1(intsign,intowner){return[sign,owner](TaskWrap&tw1,TaskWrap&tw2){std::cout<<"run task1 sign:"<<sign<<std::endl;};}// create task2autocreateTask2(intsign,intowner){return[sign,owner](TaskWrap&tw1,TaskWrap&tw2){std::cout<<"run task2 sign:"<<sign<<std::endl;};}// create task3autocreateTask3(conststd::string&id,intt1,intt2){return[id,t1,t2](TaskWrap&tw1,TaskWrap&tw2){std::cout<<"run task3 id:"<<id<<" and run "<<t1<<" - "<<t2<<std::endl;};}};intmain(){TaskGenerator tg;TaskWrap runner;TaskWrap worker;autohBrush=tg.createTask1(1,2);autohWash=tg.createTask2(5,6);autohDress=tg.createTask3("dress",7,8);runner.addTask("brush",hBrush);runner.addTask("wash",hWash);runner.addTask("dress",hDress);runner.runTask("brush",worker);runner.runTask("wash",runner);runner.runTask("dress",runner);return0;}

六、总结

古人经常说“天下文章一大抄”,这开发语言之间其实也是类似。都是发现某些问题在当前语言无法方便快捷的处理的情况下又创建了一个新的语言。C++的特点就在于其高度的底层性和灵活性,可以模拟实现各种具体的高级语言的特性,这本来是它的优势,但为了实现这些特性,其过程变得相当复杂。这反而让很多开发者认为C++难于掌握,正所谓优势即劣势,这就是事物的两个方面。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/12 20:48:41

抖音视频批量下载完整指南:从入门到精通

抖音视频批量下载完整指南&#xff1a;从入门到精通 【免费下载链接】douyin-downloader 项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader 还在为无法高效保存抖音精彩内容而烦恼吗&#xff1f;想要建立个人专属的视频资源库吗&#xff1f;今天介…

作者头像 李华
网站建设 2026/4/30 7:08:09

ncmToMp3:免费解锁网易云音乐格式转换的终极解决方案

ncmToMp3&#xff1a;免费解锁网易云音乐格式转换的终极解决方案 【免费下载链接】ncmToMp3 网易云vip的ncm文件转mp3/flac - ncm file to mp3 or flac 项目地址: https://gitcode.com/gh_mirrors/nc/ncmToMp3 还在为网易云VIP下载的音乐只能在特定App中播放而苦恼吗&am…

作者头像 李华
网站建设 2026/4/19 2:33:32

Qwen3-Embedding-4B内存泄漏?生产环境监控部署

Qwen3-Embedding-4B内存泄漏&#xff1f;生产环境监控部署 1. 背景与问题引入 在当前大规模语言模型广泛应用的背景下&#xff0c;向量嵌入服务已成为信息检索、语义匹配和推荐系统等核心场景的重要基础设施。Qwen3-Embedding-4B作为通义千问最新推出的中等规模嵌入模型&…

作者头像 李华
网站建设 2026/5/1 7:48:30

零基础掌握ComfyUI视频合成:VHS_VideoCombine节点完全教程

零基础掌握ComfyUI视频合成&#xff1a;VHS_VideoCombine节点完全教程 【免费下载链接】ComfyUI-VideoHelperSuite Nodes related to video workflows 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-VideoHelperSuite 在AI绘画创作的世界里&#xff0c;将静态图…

作者头像 李华
网站建设 2026/5/1 5:44:02

终极防休眠工具:让Windows永远保持清醒的完美解决方案

终极防休眠工具&#xff1a;让Windows永远保持清醒的完美解决方案 【免费下载链接】NoSleep Lightweight Windows utility to prevent screen locking 项目地址: https://gitcode.com/gh_mirrors/nos/NoSleep 你是否经历过这些让人抓狂的时刻&#xff1f;&#x1f494; …

作者头像 李华
网站建设 2026/5/1 8:02:25

绝区零一条龙:5分钟快速上手完全免费自动化工具

绝区零一条龙&#xff1a;5分钟快速上手完全免费自动化工具 【免费下载链接】ZenlessZoneZero-OneDragon 绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄 项目地址: https://gitcode.com/gh_mirrors/ze/ZenlessZoneZero-OneDragon 绝区零一条龙是…

作者头像 李华