当前位置: 首页 > news >正文

【effective C++】条款四十四:将与参数无关的代码抽离 templates

文章目录

  • Effective C++ 条款44:将与参数无关的代码抽离templates
    • 核心思想:对抗"代码膨胀"
    • 规则详解与示例
      • 规则1:不与造成膨胀的参数相依
      • 规则2:处理非类型参数造成的膨胀
      • 规则3:处理类型参数造成的膨胀
    • 优化带来的权衡
    • 总结

请记住:
1. Templates 生成多个 classes 和多个函数,所以任何 template 代码都不该与某个造成膨胀的 template 参数产生相依关系。
2. 因非类型模板参数(non-type template parameters)而造成的代码膨胀,往往可消除,做法是以函数参数或 class 成员变量替换 template 参数
3. 因类型参数(type parameters)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(binary representations)的具现类型(instantiation types)共享实现码

toolName: CompactFake

status: success

Effective C++ 条款44:将与参数无关的代码抽离templates

核心思想:对抗"代码膨胀"

条款44的核心目标是避免因模板使用不当而引发的代码膨胀。模板本质上是一种"代码生成器",编译会为每一组不同的模板参数生成一份独立的代码。如果这些代码中存在与模板参数无关的部分,就会造成冗余。

规则详解与示例

规则1:不与造成膨胀的参数相依

这是总纲。它提醒你要有意识地去审视模板代码,识别出哪些部分其实并不依赖于那些"会引发多种实例化"的参数(无论是类型参数还是非类型参数)。

规则2:处理非类型参数造成的膨胀

非类型参数指整数、枚举、指针等具体值,而非类型。

问题:一个矩阵模板,尺寸作为非类型参数。Matrix<int, 5>Matrix<int, 10>会导致编译器生成两个几乎完全相同的invert函数,仅仅因为尺寸常量不同。

// 膨胀的写法template<typenameT,size_t n>classSquareMatrix{public:voidinvert(){/* 算法实现,但循环次数依赖于 n */}};

解法:将"与参数无关的代码抽离template"。具体做法是引入一个基类,将尺寸作为运行时参数传递。

// 1. 创建与尺寸无关的基类template<typenameT>classSquareMatrixBase{protected:// 尺寸不再是模板参数,而是函数参数voidinvert(size_t matrixSize){/* 算法实现 */}};// 2. 子类调用基类实现template<typenameT,size_t n>classSquareMatrix:privateSquareMatrixBase<T>{// 私有继承,表示"用...来实现"private:usingSquareMatrixBase<T>::invert;// 防止名称遮掩public:// 子类的invert只是封装一下,调用基类的实现voidinvert(){this->invert(n);}// 注意用this->确保找到基类名称};

这样,所有SquareMatrix<int, n>对象都共享同一份SquareMatrixBase<int>::invert代码,从根本上消除了因尺寸n不同造成的膨胀。

规则3:处理类型参数造成的膨胀

类型参数造成的膨胀更隐蔽,但同样可以优化。

问题:在某些平台上,int*long*的底层二进制表示完全相同,但List<int*>List<long*>会实例化成两份完全独立的代码。

解法:让具有相同二进制表示的类型共享实现码。常见的做法是让模板类内部使用一个以void*进行操作的通用实现,然后通过类型安全的接口封装给用户。

// 1. 通用实现,操作void*classGenericPointerList{protected:voidinsert(void*p);// ...};// 2. 类型安全模板接口template<typenameT>classPointerList:privateGenericPointerList{public:voidinsert(T*p){GenericPointerList::insert(static_cast<void*>(p));}// ...};

这样,PointerList<int*>PointerList<long*>虽然类型不同,但底层的实现都共用GenericPointerList的代码,避免了膨胀。

优化带来的权衡

需要注意的是,这些优化技术并非没有代价。以非类型参数的优化为例:

  • 优点:显著减少代码体积,可能提升指令缓存命中率,使程序运行更快。
  • 代价:将编译期常量(如模板参数n)变为运行期参数或成员变量,编译器可能无法进行像"常量传播"这样的深度优化,理论上可能降低代码运行效率。同时,引入基类可能会增加对象大小(例如基类中存储数据指针)。

因此,是否要进行这类优化,需要根据实际情况权衡。如果代码体积是瓶颈,或者矩阵尺寸很大且算法复杂,那么优化利大于弊。如果是对性能极其敏感的循环,保留编译期优化可能是更好的选择。

总结

简单来说,条款44教你像优化重复函数一样去优化模板:

  1. 树立意识:时刻警惕模板可能引起的代码膨胀。
  2. 针对非类型参数:考虑能否把它变成函数参数或成员变量,从而将代码实现"下沉"到与参数无关的基类中。
  3. 针对类型参数:检查是否有二进制表示相同的类型(特别是指针),可以考虑让它们共享同一份底层实现。

在学习C++模板时,理解这种空间与效率的权衡是非常重要的成长。

http://www.jsqmd.com/news/473771/

相关文章:

  • (104页PPT)DG1067全面企业绩效管理(附下载方式)
  • 计算机领域Top期刊盘点:从CSCD目录看学术论文发表策略
  • CPS、CPA、CPL、CPC 是什么?联盟营销 4 大模式一次读懂
  • 架构设计师论文框架
  • 计算机毕业设计springboot汽车租赁系统 SpringBoot框架下智能车辆分时租赁与调度服务平台 基于Java Web的共享汽车在线预约与运维管理系统
  • 别再死磕 Word 了!Paperzz 毕业论文初稿:让你从选题到成稿,只花 10 分钟搞定✅
  • SpringBoot分层概念澄清 DTO、BO、VO
  • 【CVPR 2025】ROD-MLLM:迈向更可靠的多模态大型语言模型中的目标检测
  • MySQL【表的约束上】
  • Intel RealSense D455 在ARM64 (Jetson) 平台上 ubuntu22.04下使用笔记
  • 2026杭电多校春季训练赛日志
  • 【effective c++】条款四十五:运用成员函数模板接受所有兼容类型
  • 安卓wakelock 学习
  • 从空白文档到完整初稿:Paperzz 如何让毕业论文写作「零门槛」通关?
  • 创新GL微电网二次控制:基于事件触发的下垂控制及其最小事件触发间隔的扰动补偿研究“(具有参考文...
  • if-else条件语句详解
  • 【深度学习代码流程】李宏毅机器学习HW-1:预测美国COVID-19阳性病率
  • MATLAB/Simulink永磁直驱风力发电系统:SVPWM空间电压矢量调制与双闭环解耦控制应用
  • 从选题到成稿零焦虑:Paperzz 毕业论文初稿写作,让学术创作告别 “卡壳式内耗”
  • 开关磁阻电机电流斩波控制仿真 simulink仿真 双闭环控制等 含有文档报告,详细的参数说明
  • Vue3 + Vite 局域网 HTTPS 访问实战:手机秒连本地开发环境
  • 2026 学术写作破局:Paperzz 如何用「四步闭环法」解决毕业论文初稿难产,让你 3 天写完合格初稿
  • 【软件测试】系统学习清单(含知识点+掌握程度拆解)
  • # Vue 实现 PDF 预览与批量打印组件
  • 论文党「反内耗」神器:Paperzz 把毕业论文初稿写成「开卷答题」,4 步搞定从 0 到成稿
  • OpenClaw Skill 操作钉钉(原理+20个实例)
  • 数据预处理骚操作
  • 自动化仓储系统的核心设备堆垛机最怕啥?急起急停带来的机械冲击。老司机都知道S型曲线速度控制才是王道,今天咱就扒一扒西门子S7-1500里的实战代码
  • 高通跃龙QCS9100平台上工业缺陷检测实战(1): 从摄像头到端侧推理的最小闭环
  • 实测负荷数据(示例)