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

2024-01-18 17:44

本文主要是介绍条款44:将与参数无关的代码抽离templates,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.前言

Template是个节省时间和避免代码重复的一个方法。不再需要键入20个类似的classes而每一个带有15个成员函数,只需要键入一个class template,留给编译器去具现化那20个你需要的相关classes和300个函数。(class template的成员函数只有在被使用时才被暗中具现化,只有在这300个函数的每一个都被使用,你才会获得这300个函数),function template有类似的诉求。替换写许多函数,只需写一个function template,然后让编译器做剩余的事情。

但如果不小心,使用template可能会导致代码膨胀:其二进制码带着重复的代码,数据。其结果可能是源码看起来合身且整齐,但目标码(object code)却不是那么一回事。

2.实例分析

当你编写某个函数,而你明白其中某些部分的实现码和另外一个函数的实现码实质相同,我们一般会抽象出两个函数的共同部分,把它放进第三个函数中,然后令原先两个函数调用这个新函数。同样的道理,如果你正在编写某个class,而其中的某些部分和另外一个class的某些部分相同,你也不会重复这共同的一部分,取而代之的是你会把共同部分搬移到新的class去,然后使用继承或复合,令原先的1classes取用这共同特性。而原的classes的互异部分仍然留在原位置不动。

编写templates时,也是做相同的分析,以相同的方式避免重复,但其中有个窍门。在non-template代码中,重复十分明确:你可以看到两个函数或两个classes之间有所重复。然而在template代码中,重复是隐晦的:毕竟只有一份template源码,所以必须训练自己去感受当template被具现化时可能发生的重复。

举个例子,假设你想为固定尺寸的正方矩阵编写一个temolate。该矩阵的形式之一是支持逆矩阵运算(matrix inversion)。

template<typename T,std::size_t n>//template支持n,x,n矩阵,//元素是类型T的objects;见以下关于//size_t参数信息
class SquareMatrix{    public:...void invert();//求逆矩阵};           

这个template接受一个类型参数T,除此以外还接受一个类型为size_t的参数那是个非类型参数(non-type parameter)。这种参数和类型参数比较起来不常见,但它们完全合法,就比如本例:

SquareMatrix<double,5> sml;
...
sml.invert();//调用SquareMatrix<double,5>::invert
SquareMatrix<double,10> sm2;
...
sm2.invert();//调用SquareMatrix<double,10>::invert

这会具象化两份invert,这些函数并非完完全全相同,因为其中一个操作的是5*5矩阵而另一个操作的是10*10矩阵,但除来常量5和10,两个函数的其它部分完全相同。这是template引出代码膨胀的一个典型例子。

如果你能看到两个函数完全相同,除了使用5和使用10的区别,本能是为它们建立一个带数值参数的函数,然后以5和10来调用这个带参数的函数,而不重复代码。下面是对SquareMatrix的第一次修改:

template<typename T>
class SquareMatrixBase{protected:...void invert(std::size_t matrixSize);//以给定的尺寸求逆矩阵...};
tempalate<typename T,std::size_t n>
class SquareMatrix:private SquareMatrixBase<T>
{private:using SquareMatrixBase<T>::invert;//避免遮掩base 版本的invertpublic:...void invert() {this->invert(n);}
};

正如所看到的,带参数的invert位于base class SquareMatrixBase中,和SquareMatrix一样,SquareMatrixBase也是个template,不同的是它只对”矩阵元素对象的类型“参数化,不对矩阵的尺寸参数化。因此对于给定之元素对象类型,所有矩阵共享同一个SquareMatrixBase class。它们也将因此共享这唯一一个class内的invert。

SquareMatrixBase::invert只是企图成为“避免derived class代码重复”的一种方法,所以它以protected替换public。调用它造成的额外成本应该是0,因为derived classes的inverts调用base class版本时用的是Inline调用。这些函数使用“this->”记号,假设不这样做,模板化基类内的函数名称会被derived classes掩盖。也请注意SquareMatrix和SquareMatrixBase之间的继承关系是private。这反映一个事实:这里的base class只是为了帮助derived class实现,不是为了表现SquareMatrix和SquareMatrixBase之间的is-a关系。

当然,这里目前还存在一些问题:SquareMatrixBase::invert如何知道该数据是什么数据,虽然可以从参数中知道矩阵尺寸,但如何知道哪个特定的矩阵的数据在哪?想必只有derived class知道,Derived class如何联络其base class做逆运算操作动作。一个可能的做法是为了SquareMatrixBase::invert添加另一个参数,也许是个指针,指向一块用来放置矩阵数据的内存起点,这个方法行得通。但invert不是唯一一个可写为“形式与尺寸无关并可移动到SquareMatrixBase内的SquareMatrixBase内“的SquareMatrix函数。如果有若干这样的函数,我们唯一要做的就是找出保存矩阵元素值得那块内存。我们可以对所有这样得函数添加一个额外得参数,却得一次又一次地告诉SquareMatrixBase相同地信息,这样似乎不好。

另一个办法是令SquareMatrixBase储存一个地址,指向矩阵数值所在得内存。而它只要存了那些东西,就可能存储矩阵得大小。类似这样:

template<typename T>
class SquareMatrixBase{protected:SquareMatrixBase(std::size_t,T* pMem):size(n),pData(pMem)//存矩阵大//小和一个指针,指向矩阵数值{}void setDataPtr(T* ptr){pData=ptr;}....private:std::size_t size;//矩阵大小T* pData;//指针,指向矩阵内容
};

这允许derived classes决定内存分配方式。某些实现版本也许会决定将矩阵数据存储在SquareMatrix对象内部:

template<typename T,std::size_t n>
class SquareMatrix:private SquareMatrixBase<T>
{public:SquareMatrix():SquareMatrixBase<T>(n,data) { }//送出矩阵大小和数据指针给base class....private:T data[n*n];};

这种类型的对象不需要动态分配内存,但对象自身可能性非常大,另一种做法是把每一个矩阵的数据放进heap(也就是通过new 来分配内存);

template<typename T,std::size_t n>
class SquareMatrix:private SquareMatrixBase<T>
{public:SquareMatrix():SquareMatrixBase<T>(n,0),pData(new T[n*n])//将base 的数据指针设为null{                                                    //为矩阵分配内存this->setDataPtr(pData.get());//将它的一个副本交给base class}private:boost::scoped_array<T> pData;
}

3.总结

(1)Template生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生联系;

(2)因非类型模板参数而造成的代码膨胀,往往可以消除,做法是以函数参数或class成员变量替换template参数

(3)因类型参数(type parameters)而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述的具现类型共享实现码。

这篇关于条款44:将与参数无关的代码抽离templates的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/619764

相关文章

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

4B参数秒杀GPT-3.5:MiniCPM 3.0惊艳登场!

​ 面壁智能 在 AI 的世界里,总有那么几个时刻让人惊叹不已。面壁智能推出的 MiniCPM 3.0,这个仅有4B参数的"小钢炮",正在以惊人的实力挑战着 GPT-3.5 这个曾经的AI巨人。 MiniCPM 3.0 MiniCPM 3.0 MiniCPM 3.0 目前的主要功能有: 长上下文功能:原生支持 32k 上下文长度,性能完美。我们引入了

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时