網(wǎng)站首頁 編程語言 正文
模板與泛型編程淺談
摘要(Effective C++):
? C++template的最初發(fā)展動(dòng)機(jī)很直接:讓我們得以建立“類型安全”的容器如vector,list和map。然而當(dāng)愈多人用上templates時(shí),他們發(fā)現(xiàn)template有能力完成愈多可能的變化。容器當(dāng)然很好,但泛型編程(generic programming)——寫出的代碼和其所處理的對象類型彼此獨(dú)立——更好。STL算法如for_each,find和merge就是這一類編程的結(jié)果。最終人們發(fā)現(xiàn),C++template機(jī)制自身是一部完整的圖靈機(jī):它可以用來計(jì)算任何可計(jì)算的值。于是導(dǎo)出了模板元編程(template mataprogramming),創(chuàng)造出“在C++編譯器內(nèi)執(zhí)行并于編譯完成時(shí)停止執(zhí)行”的程序。
模板與泛型編程簡單介紹
? 面向?qū)ο缶幊蹋∣OP)和泛型編程都可以處理編寫程序時(shí)不知道類型的情況;二者的不同之處在于:OOP能處理類型在程序運(yùn)行之前都未知的情況;而在泛型編程中,在編譯時(shí)就能獲知類型了
? 我們所常用的STL標(biāo)準(zhǔn)庫中,每一個(gè)容器都提供了單一的,泛型的定義,例如我們所常用的vector,我們可以定義很多類型的vector
vector<int> vi; // vi是裝載int類型的vector容器的實(shí)例 vector<string> vs; // vs是裝載string類型的vector容器的實(shí)例 vector<double> vd; // vd是裝載double類型的vector容器的實(shí)例
模板是泛型編程的基礎(chǔ),一個(gè)模板就是一個(gè)創(chuàng)建類或者函數(shù)的藍(lán)圖或者公式
函數(shù)模板
// 簡單的比較函數(shù)模板 template<typename T> int cmp(const T& v1,const T& v2) { if(v1<v2) return -1; else if(v1>v2) return 1; else return 0; }
函數(shù)定義以關(guān)鍵字template開始,后跟一個(gè)模板參數(shù)列表,這是一個(gè)逗號(hào)分隔的一個(gè)或多個(gè)模板參數(shù)的列表,用尖括號(hào)包圍起來
**注:**在模板定義中,模板參數(shù)列表不能為空
模板參數(shù)列表表示在類或函數(shù)定義中用到的類型或者值。當(dāng)我們使用模板的時(shí)候,我們可以(顯式或隱式地)指定模板實(shí)參,將其綁定到模板參數(shù)上
簡單了解模板的實(shí)例化過程
? 眾所周知,當(dāng)你覺得模板編程十分智能的時(shí)候,一定是有東西在為你負(fù)重前行,C++提供了模板與泛型編程的這個(gè)能力,這便意味著有一個(gè)東西在為你動(dòng)態(tài)地實(shí)現(xiàn)模板的功能,而這一定是比C++這個(gè)高級(jí)語言層面更為底層的東西,而我們所了解的知識(shí)中,比C++高級(jí)語言較為底層的東西,除了操作系統(tǒng),便是編譯器了。
? 當(dāng)我們調(diào)用一個(gè)函數(shù)模板的時(shí)候,編譯器(通常)用函數(shù)實(shí)參來為我們推斷模板實(shí)參。簡單來講,便是我們在調(diào)用函數(shù)模板的時(shí)候,編譯器通過使用實(shí)參的類型來確定綁定到模板參數(shù)T的類型
cout<<cmp(1,0)<<endl; // T為int
在上訴代碼中,函數(shù)cmp的實(shí)參類型是int,編譯器便會(huì)推斷出模板實(shí)參為int,并將它綁定到模板參數(shù)T上
簡單來說,編譯器用推斷出的模板參數(shù)來為我們實(shí)例化一個(gè)特定版本的函數(shù)
模板編譯
當(dāng)編譯器遇到一個(gè)模板定義的時(shí)候,它并不會(huì)生成代碼。只有我們實(shí)例化出模板的一個(gè)特定的版本時(shí),編譯器才會(huì)生成其對應(yīng)的代碼。當(dāng)我們使用(而不是定義)模板時(shí),編譯器才會(huì)生成代碼。這個(gè)特性影響我們?nèi)绾谓M織代碼以及錯(cuò)誤何時(shí)才可以被檢測到
通常來說,我們將類定義和函數(shù)說明放在頭文件中,而普通函數(shù)和類的成員函數(shù)的定義放在源文件中
模板則不盡相同:為了生成一個(gè)實(shí)例化的版本,編譯器需要掌握函數(shù)模板或類模板成員函數(shù)的定義
總結(jié):與非模板代碼不同,模板的頭文件通常既包括聲明也包括定義,即函數(shù)模板和類模板成員函數(shù)的定義通常放在頭文件中
大多數(shù)編譯錯(cuò)誤出現(xiàn)的時(shí)機(jī)?
- 第一階段,編譯模板本身時(shí),該時(shí)期所出現(xiàn)的錯(cuò)誤大多數(shù)為語法錯(cuò)誤
- 第二階段,編譯器遇到模板使用時(shí)
- 第三階段,模板實(shí)例化時(shí),而只有在這個(gè)階段才能發(fā)現(xiàn)類型相關(guān)的問題
**注意事項(xiàng):**保證傳遞給模板的實(shí)參支持模板所要求的操作,以及這些操作在模板中能正確的工作,是調(diào)用者的責(zé)任
類模板
??類模板是用來生成類的藍(lán)圖的。與函數(shù)模板不同之處是,編譯器不能為類模板推斷模板參數(shù)類型。?所以我們必須在模板名后的尖括號(hào)中提供額外的信息——用來替代模板參數(shù)的模板實(shí)參列表
vector<int> vi; deque<double> dd; pair<string,int> key_val;
定義類模板
template<typename T> class T_vector { public: typedef T value_type; // 構(gòu)造函數(shù) T_vector() =default; T_vectot(std::initializer_list<T> il); // 容器的元素?cái)?shù)目 size_type size() const { return data->size(); } bool empty() const { return data->empty(); } // 添加元素 void push_back(const T& val) { data->push_back(val); } void push_back(T &&val) { data->push_back(std::move(val)); } private: std::shared_ptr<std::vector<T> > data; // 若data[i]無效,則拋出msg void check(size_type i,const std::string &msg) const; }
類似函數(shù)模板,類模板以關(guān)鍵字template開始,后跟模板參數(shù)列表。在類模板(及其成員)的定義中,我們將模板參數(shù)當(dāng)作替身,代替使用模板時(shí)用戶需要提供的類型或值
**注:**一個(gè)類模板的每一個(gè)實(shí)例都形成一個(gè)獨(dú)立的類,而類模板的每個(gè)實(shí)例都有其自己版本的成員函數(shù)
? 所以,我們可能會(huì)出現(xiàn)一個(gè)單一模板并不能滿足所有類型的需求,而模板特例化就出現(xiàn)了
類模板成員函數(shù)的實(shí)例化
? 默認(rèn)的情況下,一個(gè)類模板的成員函數(shù)只有在程序用到它的時(shí)候才會(huì)實(shí)例化
// 實(shí)例化T_vector和接受initializer_list<int>的構(gòu)造函數(shù) T_vector<int> T_vi = { 0,1,2,3,4,5 };
如果一個(gè)成員函數(shù)沒有被使用,則它將不會(huì)被實(shí)例化
為什么我們需要模板特例化?
當(dāng)我們編寫單一的模板時(shí),使其對任何可能的模板實(shí)參都是最合適的,都能實(shí)例化,但者往往都是過于理想化的情況。在某些特殊的情況下,通用的模板的定義可能對特定的類型是不合適的,通用定義的模板可能會(huì)出現(xiàn)編譯失敗或者做得不夠完善的情況。
??故,當(dāng)我們不能(或者不希望)使用模板版本的時(shí)候,可以定義類或函數(shù)模板的一個(gè)特例化版本
定義函數(shù)模板特例化
// 原先cmp函數(shù)的特殊版本,用來處理特殊的字符數(shù)組的指針template<>int cmp(const char* const& p1,const char* const& p2) { return strcmp(p1,p2);}// 原先cmp函數(shù)的特殊版本,用來處理特殊的字符數(shù)組的指針 template<> int cmp(const char* const& p1,const char* const& p2) { return strcmp(p1,p2); }
函數(shù)重載與模板特例化的區(qū)別
? 當(dāng)定義函數(shù)模板的特例化版本時(shí),我們本質(zhì)上接管了編譯器的工作。即,我們?yōu)樵鹊哪0宓钠渲幸粋€(gè)特殊的實(shí)例提供了定義。簡而言之,特例化的本質(zhì)是實(shí)例化一個(gè)模板,而非重載它,因此特例化并不影響函數(shù)匹配
注意事項(xiàng):
- 為了特例化一個(gè)模板,原模版的聲明必須在作用域中
- 在任何使用模板實(shí)例的代碼之前,特例化版本的聲明也必須在作用域中
- 所有同名模板的聲明應(yīng)該放在前面,然后是這些模板的特例化版本
類模板部分特例化
與函數(shù)模板不同的是,類模板的特例化不必為所有模板參數(shù)提供實(shí)參。一個(gè)類模板的部分特例化本身是一個(gè)模板,使用它時(shí)用戶還必須為那些在特例化版本中指定的模板參數(shù)提供實(shí)參
注:我們只能部分特例化類模板,而不能部分特例化函數(shù)模板
總結(jié)
原文鏈接:https://blog.csdn.net/weixin_51958878/article/details/123852228
相關(guān)推薦
- 2022-04-09 python中IO流和對象序列化詳解_python
- 2022-10-20 C#?Winform消息通知系統(tǒng)托盤氣泡提示框ToolTip控件_C#教程
- 2022-12-01 C語言數(shù)據(jù)在內(nèi)存中的存儲(chǔ)流程深入分析_C 語言
- 2023-01-05 Python?Flask?模型介紹和配置方法_python
- 2023-07-04 maven引入本地jar,打包問題
- 2022-04-02 Python字符串與正則表達(dá)式詳細(xì)介紹_python
- 2021-11-08 C++繼承模式詳解_C 語言
- 2022-06-18 C#讀寫Config配置文件案例_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支