类型列表与编译期算法:手写 typelist 的 map/filter
写模板代码时常有这样的需求手里有一组类型int, double, char, long想「给每个类型生成一个成员变量」「把每个类型变成它的指针」「只留下整型」…… 这些运算发生在编译期操作对象是「类型」而不是「值」。C 里表示「一组类型」的惯用法叫 typelist类型列表——它就是一个templateclass... struct typelist {}的空壳真正的功夫全在编译期算法上。这些算法在boost::mp11、std::tuple的实现里到处都是读懂它才能看懂那些库。1. 引子为什么需要「类型列表」std::vectorint存的是「一堆 int 值」而 typelist 存的是「一堆类型」。它本身不占内存、不产生任何对象纯粹是给编译器看的。最典型的应用场景tuple 的迭代、variant 的类型集合、给每个类型生成一个字段的序列化框架。比如你想写一个结构体它的成员是int、double、string各一份模板代码里就需要先「拿到这组类型」再逐个展开。typelist 就是那个「拿在手里的类型集合」。templatetypename...Tsstructtypelist{};// 空壳只「携带」类型不产生任何对象usingMyTypestypelistint,double,char,long;一个 typelist 就是一个类型里面的Ts...是类型参数包parameter pack。要对它做运算就要用**偏特化partial specialization**去「解包」它。官方文档类型参数包与包展开parameter pack —— typelist 和所有变参模板的根基。2. 基础算法length / at / append这三个是最基本的「列表操作」套路统一声明主模板 → 偏特化匹配typelistTs...→ 用sizeof...(Ts)或递归解出结果。#includecstdio#includecstddef#includetype_traitstemplatetypename...Tsstructtypelist{};// length类型个数templatetypenameListstructlength;templatetypename...TsstructlengthtypelistTs...{staticconstexprstd::size_t valuesizeof...(Ts);};// at第 N 个类型递归N 每层减 1同时砍掉头部templatestd::size_t N,typenameListstructat;templatetypenameHead,typename...Tailstructat0,typelistHead,Tail...{usingtypeHead;};// 递归基N0templatestd::size_t N,typenameHead,typename...TailstructatN,typelistHead,Tail...{usingtypetypenameatN-1,typelistTail...::type;};// append尾部追加一个类型templatetypenameList,typenameTstructappend;templatetypename...Ts,typenameTstructappendtypelistTs...,T{usingtypetypelistTs...,T;};intmain(){usingLtypelistint,double,char,long;static_assert(lengthL::value4,长度应为 4);static_assert(std::is_same_vtypenameat0,L::type,int,第 0 个是 int);static_assert(std::is_same_vtypenameat2,L::type,char,第 2 个是 char);usingL2typenameappendL,float::type;static_assert(lengthL2::value5,追加后长度 5);static_assert(std::is_same_vtypenameat4,L2::type,float,第 4 个是 float);std::puts(所有 static_assert 通过length / at / append 正确);}所有 static_assert 通过length / at / append 正确要点atN, typelistHead, Tail...的递归是「每层丢掉 HeadN 减 1」直到N0时命中递归基返回当前的 Head。这就是函数式编程里「头尾分解」在模板里的翻版。官方文档偏特化partial specialization —— 解包 typelist 必须靠它at的递归基at0, ...就是更特化的匹配。3. 核心map 与 filter有了头尾分解就能写「高阶」编译期算法map对每个类型套一个元函数F得到新列表。maptypelistint,char, add_pointer→typelistint*,char*。filter只保留满足谓词Pred的类型。filtertypelistint,char,double, is_integral→typelistint,char。#includecstdio#includecstddef#includetype_traitstemplatetypename...Tsstructtypelist{};// prepend头部插入filter 回溯时用它把「保留」的 Head 拼回去templatetypenameList,typenameTstructprepend;templatetypename...Ts,typenameTstructprependtypelistTs...,T{usingtypetypelistT,Ts...;};// map对每个类型应用元函数 F取 FT::typetemplatetypenameList,templatetypenameclassFstructmap;templatetypename...Ts,templatetypenameclassFstructmaptypelistTs...,F{usingtypetypelisttypenameFTs::type...;};// filter递归保留满足 Pred 的类型templatetypenameList,templatetypenameclassPredstructfilter;templatetemplatetypenameclassPredstructfiltertypelist,Pred{usingtypetypelist;};// 递归基空列表templatetypenameHead,typename...Tail,templatetypenameclassPredstructfiltertypelistHead,Tail...,Pred{usingresttypenamefiltertypelistTail...,Pred::type;// 先处理尾巴usingtypestd::conditional_tPredHead::value,typenameprependrest,Head::type,rest;};intmain(){usingLtypelistint,char,double,long,float;usingPtypenamemapL,std::add_pointer::type;static_assert(std::is_same_vP,typelistint*,char*,double*,long*,float*,map 后应全是指针);usingItypenamefilterL,std::is_integral::type;static_assert(std::is_same_vI,typelistint,char,long,filter 后应只剩整型);usingIPtypenamemapI,std::add_pointer::type;static_assert(std::is_same_vIP,typelistint*,char*,long*,map(filter) 组合整型指针列表);std::puts(所有 static_assert 通过map / filter 正确);}所有 static_assert 通过map / filter 正确map一步到位直接typelisttypename FTs::type...用包展开套用F。filter则必须递归先递归处理尾巴Tail...得到rest再根据PredHead::value决定要不要用prepend把Head插回头部。递归的展开过程如下filter typelistint, char, double, std::is_integral 的递归展开 filterint, char, double ├─ Head int is_integralint true → 插回头部 └─ filterchar, double ├─ Head char is_integralchar true → 插回头部 └─ filterdouble ├─ Head double is_integraldouble false → 丢弃 └─ filter 递归基返回 typelist 回溯typelist → typelistchar → typelistint, char 最终结果注意回溯的顺序因为每一步都用prepend往头部插所以结果保持了原列表的相对顺序。这是 filter 实现里最容易写反的地方用append会得到反序列表。官方文档std::conditional编译期 if —— filter 里「保留还是丢弃」靠它做编译期分支。4. typelist 与 std::tuple 的关系两者长得像但分工完全不同维度typeliststd::tuple存的是什么纯类型没有值类型对应的实例值运行时机纯编译期零运行时开销运行期对象能否 sizeof否不是对象是占内存典型用途类型运算map / filter / at存异构数据、多返回值、参数打包转换to_tupletypelistTs...::type std::tupleTs...std::tuple_element_t取回类型简单说typelist 是「类型的清单」tuple 是「把清单落地成值」。很多库比如boost::mp11就是先用 typelist 算好类型再转成 tuple 或结构体去用。5. 落地把 typelist 变成能用的东西typelist 本身不能跑最终要「落地」。下面是两个典型落地方式转成std::tuple以及在编译期对类型做聚合计算这里用折叠表达式求所有类型sizeof之和。#includecstdio#includecstddef#includetuple#includetype_traitstemplatetypename...Tsstructtypelist{};// typelist - std::tupletemplatetypenameListstructto_tuple;templatetypename...Tsstructto_tupletypelistTs...{usingtypestd::tupleTs...;};// 编译期聚合所有类型的 sizeof 之和C17 折叠表达式templatetypenameListstructtotal_size;templatetypename...Tsstructtotal_sizetypelistTs...{staticconstexprstd::size_t value(sizeof(Ts)...std::size_t{0});};intmain(){usingLtypelistint,double,char,long;usingTtypenameto_tupleL::type;static_assert(std::is_same_vT,std::tupleint,double,char,long,typelist 应能落地成 tuple);T t{42,3.14,x,99L};std::printf(tuple 元素个数: %zu\n,std::tuple_size_vT);std::printf(第 0 个元素: %d\n,std::get0(t));std::printf(第 1 个元素: %.2f\n,std::get1(t));std::printf(所有类型 sizeof 之和: %zu\n,total_sizeL::value);}tuple 元素个数: 4 第 0 个元素: 42 第 1 个元素: 3.14 所有类型 sizeof 之和: 21total_size里的(sizeof(Ts) ... std::size_t{0})是 C17 的折叠表达式fold expression把「对每个类型算 sizeof 再累加」这件事一行写完——这也是 typelist 类算法在现代 C 里被逐步替代的信号。官方文档std::tuple — cppreference —— typelist 最常见的落地目标。6. 现状Concepts 与折叠表达式取代了什么手写 typelist 递归 map/filter 是 C11 时代的主流但现在有了更顺手的工具很多场景不必再手写需求老办法typelist 递归新办法对每个类型做一件事递归 偏特化折叠表达式(f(Ts), ...)编译期 ifstd::conditional/ tag dispatchif constexprC17约束模板参数SFINAE / enable_ifConceptsC20类型集合运算map/filter/at手写 typelistboost::mp11不过有两个理由仍然要读懂typelist第一boost::mp11、std::variant、std::tuple这些库内部就是这套东西读源码绕不开第二std::tuple_element_tN, Tuple、std::variant的类型运算本质就是 typelist 的at。理解了底层的「头尾分解 递归 偏特化」再看到mp_list那种高级写法就不会发怵。7. 延伸阅读类型参数包parameter pack— cppreferencetypelist 与包展开的地基偏特化partial specialization— cppreferenceat/filter的递归基就是靠它「更特化优先」匹配std::conditional — cppreference编译期分支的核心工具std::tuple — cppreferencetypelist 最常见的落地目标配合std::tuple_element就是内置的at本知识库内的相关篇目《tag dispatch 与重载偏序实战按迭代器类别派发算法》 —— tag dispatch标签派发用「空类型」当标签《表达式模板Eigen 式延迟求值把 abc 压成一次遍历》 —— 表达式模板Expression Templates让 abc 不再生成中间临时对象《编译期反射模拟C 没有反射怎么办》 —— C 至今没有运行时反射想「遍历结构体成员」「自动序列化」只能靠模拟8. 一句话总结typelist 用一个空壳templateclass... struct typelist携带一组类型length/at/map/filter 全靠「头尾分解 递归 偏特化」实现结果用static_assert在编译期验证现代 C 里它正被 Concepts、折叠表达式、if constexpr部分取代但读boost::mp11、std::tuple的源码这套手艺仍然是绕不开的底子。

相关新闻

同一个项目,算法岗和工程岗的简历该怎么分别写

同一个项目,算法岗和工程岗的简历该怎么分别写

做推荐、做风控、做 NLP 的人,投岗位时经常两边都投:算法岗和工程岗。很多人用同一份简历,结果两边都不占优。 同一个项目,两种写法的侧重完全不同。 假设这是同一件事 你在一个推荐系统里,做了召回链路的改造&#xf…

2026/10/16 14:18:23 阅读更多 →
DeepSeek医疗本地化部署:医院内网AI训练全栈实践指南

DeepSeek医疗本地化部署:医院内网AI训练全栈实践指南

简介:本资源是一份面向医疗AI工程师与医院信息科技术人员的DeepSeek大模型本地化部署实战指南,聚焦医疗数据私有化训练场景,解决敏感病历数据合规处理、本地算力适配与垂直领域模型微调等核心问题。文档以PDF格式交付,共1个文件&a…

2026/10/16 14:17:21 阅读更多 →
论文AI率太高咋降才稳妥?2026年保姆级降AIGC流程:实用指令亲测揭秘+3款工具横评

论文AI率太高咋降才稳妥?2026年保姆级降AIGC流程:实用指令亲测揭秘+3款工具横评

花了整整三个月打磨的论文,送到学校AI检测系统一查,AI占比居然高达65%!实在百口莫辩,那些观点推导、文献引用,全是我翻遍几十份文献熬了无数夜攒出来的! 为了把居高不下的AI占比压到合格线内,我…

2026/10/16 14:17:21 阅读更多 →

最新新闻

STM32+MPU6050四元数姿态解算:从I2C读取到互补滤波实战

STM32+MPU6050四元数姿态解算:从I2C读取到互补滤波实战

简介:针对STM32F103与MPU6050的六轴姿态检测需求,这套源码工程完整实现了基于四元数的姿态解算流程。项目由MPU6050采集加速度与角速度数据,通过IO模拟I2C与主控通信,STM32端完成四元数积分、归一化及重力向量校正,并借…

2026/10/16 14:54:08 阅读更多 →
网神SecADS 3600应用交付系统快速配置手册:从VLAN到负载均衡实战

网神SecADS 3600应用交付系统快速配置手册:从VLAN到负载均衡实战

简介:这份资源是奇安信网神SecADS 3600应用交付系统的快速配置手册,面向网络运维工程师、售前技术支持及渠道商技术人员,帮助读者快速掌握设备部署与日常配置方法。压缩包内仅含1个docx文档,大小约1.18MB,内容以图文步…

2026/10/16 14:54:08 阅读更多 →
Docker容器化实战:从镜像分层到Compose编排的完整指南

Docker容器化实战:从镜像分层到Compose编排的完整指南

1. 为什么需要 Docker:从"环境地狱"到一次构建到处运行 先从一个最典型的场景说起。你负责的项目在本地跑得好好的,代码、依赖、中间件全都齐活,结果一到测试环境,连服务都起不来。排查半天发现,测试机上的基…

2026/10/16 14:54:08 阅读更多 →
MySQL安装配置与Pycharm连接全流程避坑指南

MySQL安装配置与Pycharm连接全流程避坑指南

这个话题我打交道的时间不短了。项目标题看起来很简单,就是“MySQL安装与配置以及使用Pycharm设置MySQL连接”,但实际操作中,大多数人恰恰是卡在别人觉得“根本不用讲”的地方:版本不知道选哪个,初始化不知道跑哪条命令…

2026/10/16 14:54:08 阅读更多 →
Qwen3-8B_rai_1.8.0_hybrid许可合规指南:MIT与Apache-2.0的区别及商用注意事项

Qwen3-8B_rai_1.8.0_hybrid许可合规指南:MIT与Apache-2.0的区别及商用注意事项

【免费下载链接】Qwen3-8B_rai_1.8.0_hybrid 项目地址: https://ai.gitcode.com/hf_mirrors/amd/Qwen3-8B_rai_1.8.0_hybrid 点击查看 免费下载 Qwen3-8B_rai_1.8.0_hybrid 是 AMD 面向 Ryzen AI 平台量化优化的 Qwen3-8B 大模型,其许可结构是典型的&qu…

2026/10/16 14:54:08 阅读更多 →
security-audit-skill:为编码智能体注入安全审计能力

security-audit-skill:为编码智能体注入安全审计能力

1. 从"security-audit-skill"这个名字说起:它到底在解决什么问题第一次看到security-audit-skill这个命名,我的直觉是:这大概率是一个面向编码智能体(coding-agent)的技能包,而不是一个独立的扫描…

2026/10/16 14:53:05 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →