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