恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

类型列表与编译期算法:手写 typelist 的 map/filter

  • 首页
  • 资讯中心
  • /
  • 类型列表与编译期算法:手写 typelist 的 map/filter

相关资讯

同一个项目,算法岗和工程岗的简历该怎么分别写 2026/10/16 14:18:22
stitch-design-taste - SKILL 2026/10/16 14:13:21
mod_ali_tts.zip实战:阿里云语音合成本地化部署与避坑指南 2026/10/16 14:13:21

最新资讯

gemma-skills新手必读:Google官方两大技能如何让AI Agent具备Gemma开发与微调能力
DeepSeek-V3-W4A8-Pruing权重文件怎么读?safetensors三分片结构与index索引完全解析
MATLAB EMD工具箱实战:从经验模态分解到HHT时频分析
教务管理系统源码解析:技术选型、数据库设计与二次开发避坑指南
E7-2 AXOS everyPON 数据服务实施:从订户模板到策略映射实战指南
gemma-skills的DPO偏好对齐实战:让Gemma 4输出你的专属风格与语气

今日推荐

Java八种基本类型深度解析:从内存布局到实战选型
基础:常用快捷键——IDEA、VS Code、PyCharm 与 Linux 终端效率速查
解决ultraedit下verilog .v文件中文注释乱码:把编码配置改到TaoToken辅助排查

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

类型列表与编译期算法:手写 typelist 的 map/filter

发布时间:2026/10/16 14:18:22
类型列表与编译期算法:手写 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的源码这套手艺仍然是绕不开的底子。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号