【C++詳解】模板進階 非類型模板參數,函數模板特化,類模板全特化、偏特化,模板分離編譯

文章目錄

  • 一、非類型模板參數
    • 應用場景
  • 二、模板的特化
    • 函數模板特化
    • 類模板特化
      • 全特化
      • 偏特化
  • 三、模板分離編譯
    • 解決方法
  • 四、模板總結


一、非類型模板參數

先前介紹的函數模板和類模板都是針對類型的類模板參數,非類型模板參數有哪些使用場景呢?我們先來看下面這個例子:

const int N = 10;template <class T>
class stack
{
public:T _a[N];int top;int capacity;
};int main()
{stack<int> s1; //10stack<int> s2; //1000return 0;
}

這是我們定義的一個靜態的棧,棧的大小由N來決定,我們想創建一個大小為10的棧s1和一個大小為1000的棧s2就會出現一個問題,由于靜態棧類型大小的每次編譯時是固定的,如果我們創建這兩個棧那么空間只能開1000,那么s1就會確定浪費990大小的空間。所以這個場景下我們就可以用非類型模板參數,它就是用一個整型常量來作為類(函數)模板的一個參數,在類(函數)模板中可以把這個參數當作常量來使用。

template <class T, size_t N> //非類型模板參數
class stack
{
public:T _a[N];int top;int capacity;
};int main()
{stack<int, 10> s1; //10stack<int, 1000> s2; //10000return 0;
}

非類型模板參數也能給缺省值,模板參數和函數參數一樣,缺省值只能從右往左給,傳實參時只能從左往右依次給,避免參數匹配的歧義性。
若模板參數是全缺省,我們創建對象時模板參數可以都不傳,但是還是要帶<>:

stack<> s3;  //全缺省

注意:
1、浮點數、類對象以及字符串是不允許作為非類型模板參數的。
2.、非類型的模板參數必須在編譯期就能確認結果。

應用場景

在這里插入圖片描述

在標準庫里array容器就用到了非類型模板參數,它是靜態數組,類似C語言的arr[ ],。功能也和arr[ ]差不多,它創建出來后也不會對成員初始化,初始化值需要用它的一個接口fill。
array和arr[ ]的本質區別是array下標訪問是用的重載函數operator[ ],所以有嚴格的越界讀和越界寫的檢查,而arr[ ]本質是指針±和指針的解引用,只有在臨界區域對越界寫有抽查,完全沒有越界讀檢查。

array<int, 10> a1;
a1.fill(1);  //初始化

在C++中我們用數組容器一般優先考慮vector,array相比vector的優勢在開大量固定大小的數組時array效率更高,因為array是在棧上開空間,在函數棧幀創建出來時空間就開好了,vector需要在堆上動態開辟。

二、模板的特化

當我們實現出的模板函數或者模板類不符合我們預期時就可以用到模板特化,模板特化與原模板深入綁定,只有當原模版存在時模板特化才會生效。簡單來說普通模板實例化是按模板生成代碼,特化本身已是具體代碼,它是為特殊類型改寫代碼,且匹配優先級比普通模板更高。

函數模板特化

函數模板特化的返回值、參數列表需與主模板替換類型后完全匹配,
我們先來看一個例子:

在這里插入圖片描述

Less對傳過來的整型變量可以準確的比較大小,而對于傳過來的整型指針變量我們本意是想讓它比較指針指向的整型變量的大小,而這里底層邏輯是比較指針變量本身的大小,所以不符合我們預期,這里就可以針對整型指針變量單開一個模板的特化版本,改寫比較邏輯。

這個時候可能有的讀者有和我當時一樣的想法,為什么不直接寫一個函數用來特殊處理int*,比如:bool Less(double* left, double* right),而是用模板特化呢?
我們要知道模板是一種泛型編程的思想,模板本意是設計出一套通用邏輯,而如果我們再創建一個普通函數,就會破壞模板本身的泛型語義一致性,使得不同類型調用Less函數時行為變得零散。模板特化就是模板為特定類型做到特殊適配,模板與特化模板是強耦合的,后續要調整通用模板的邏輯時也要考慮特化模板是否要配套修改。

我們初步設計的函數特化如下:

template<>
bool Less<int*>(int* left, int* right)
{return *left < *right;
}

注意事項
1.必須要先有一個基礎的函數模板
2. 關鍵字template后面接一對空的尖括號<>
3. 函數名后跟一對尖括號,尖括號中指定需要特化的類型
4. 函數模板特化參數要和原模版一致,如果不同編譯器可能會報一些奇怪的錯誤。
前三點很好理解,最后一點我們需要借助一個例子來理解,其實前面我們寫的普通模板并不是最最優的,我們T類型我們無法確定,可能是內置類型也可能是string或者自定義類型,如果對象一但過大這時我們原先普通模板是傳值傳參因為要拷貝效率就很低了,這里的最優解是傳引用,并且比較邏輯不改變參數最好還要加const修飾:

template<class T>
bool Less(const T& left, const T& right)
{return left < right;
}

而普通模板寫成這樣我們再寫特化版本難度就變高了,在此之前我們要先明確一點,當const修飾指針變量是當const在*左邊時是修飾的指針指向的內容不能修改,當const在*右邊時是修飾指針變量本身不能被修改。

const T* p1;   //修飾*p1
T const * p2;  //修飾*p2
T* const p3;   //修飾p3

這里我們要寫模板特化就要梳理普通模板的邏輯,因為特化版本的參數類型要和原模版參數類型嚴格一致,原模版是引用的T變量,并且const是修飾的被引用的T變量本身,我們要為int*寫一個模板特化,那么int*在邏輯上就是原模版的T,所以我們不能寫成const int*& left,因為const是修飾的int*變量指向的內容,就相當于const是修飾原模版中的*T,所以為了和原模版參數嚴格匹配這里模板特化的參數應該是int* const & left,這樣才和原模版一樣,const修飾的是引用變量本身。

template<>
bool Less<int*>(int* const & left, int* const & right)
{return *left < *right;
}

類模板特化

類模板特化就是將全部或者一部份參數確定化,模板參數全部確定化是全特化,模板參數部分確定化是偏特化,當然偏特化也可以是對參數的進一步限制。
類模板特化也需要以沒有特化過的原模版為基礎進行特化。模板名稱和模板參數結構上需要與原模板保持一致,但在成員定義、實現邏輯上可以完全不同。
全特化的匹配優先級比偏特化優先級更高,因為全特化更現成,具體用全特化還是偏特化由使用場景決定。

全特化

全特化即是將模板參數列表中所有的參數都確定化。

template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//全特化
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
};

偏特化

  • 特化部分參數
//特化部分參數
template<class T1>
class Data<T1, char>
{
public:Data() { cout << "Data<T1, char>" << endl; }
};
  • 對參數進一步限制
//對參數的進一步限制
template<class T1, class T2>
class Data<T1*, T2*>
{
public:Data() { cout << "Data<T1*, T2*>" << endl; }
};template<class T1, class T2>
class Data<T1&, T2*>
{
public:Data() { cout << "Data<T1&, T2*>" << endl; }
};

也可以兩種混著特化:

template<class T2>
class Data<int, T2*>
{
public:Data() { cout << "Data<int, T2*>" << endl; }
};

三、模板分離編譯

一個程序(項目)由若干個源文件共同實現,而每個源文件單獨編譯生成目標文件,最后將所有目標文件鏈接起來形成單一的可執行文件的過程稱為分離編譯模式。也就是我們常說的把聲明放在頭文件,把實現放在.cpp。

模板不建議分離編譯,無論的類模板還是函數模板。這是模板的定義對調用方不可見,定義方不知道調用方的使用需求導致的。
比如一個模板定義在tep.cpp,聲明在tep.h,test.cpp調用這個模板。
調用方有模板實例化成什么具體類型的信息,但是它只有模板的聲明,所以它在編譯階段無法實例化出具體代碼,因為模板要實例化必須要模板的完整定義
定義方tep.cpp有模板的完整定義,但是它沒有模板實例化成什么具體類型的信息,所以它也同樣無法在編譯階段實例化出具體代碼。
那么定義方模板在編譯期間因為沒有生成具體代碼就不會進符號表,然后鏈接階段調用方想找到調用模板的符號但是符號表里沒有,就會因為找不到符號而報錯。
歸根結底就是因為.cpp文件在鏈接之前不能交互,那么發生在鏈接之前的編譯階段就會因為信息無法交互使得模板無法生成具體代碼。

這也和內聯函數不能分離編譯的原因有有相似之處,都是因為分離編譯導致
“定義不可見”,最終鏈接階段符號缺失。但是內聯函數是因為調用處沒有內聯函數的完整定義導致函數無法展開,內聯函數又因為機制不會在定義處進符號表。模板是因為調用處沒有模板的定義無法實例化出具體代碼,定義處因為沒有具體類型而無法生成具體代碼。
類模板也一樣,類模板是將它的成員函數分離到了兩個文件。

解決方法

這個問題就是因為沒有實例化造成的,所以解決這個問題就是要讓模板在編譯階段能實例化,不管是定義方還是調用方。
1、模板定義的位置顯式實例化。這種方法不實用,不推薦使用,因為你每調用一種類型都要顯示實例化一份。所以這也驗證了模板是可以分離編譯的,只是不推薦。

template<class T>
void FuncT(const T& x)
{cout << "void FuncT(const T& x)" << endl;
}
//顯示實例化
template
void FuncT(const int& x);

2、模板的最佳實踐就是不要分離編譯,把模板直接定義在頭文件,調用模板的地方在編譯期間就有了模板的定義生成了具體代碼,填入了對應的符號,不用在鏈接期間再去找。

四、模板總結

優點
1、模板復用了代碼,節省資源,更快的迭代開發,C++的標準模板庫(STL)因此而產生
2、增強了代碼的靈活性
缺陷
1、模板會導致代碼膨脹問題,也會導致編譯時間變長(不可避免,自己寫也得寫)
2、出現模板編譯錯誤時,錯誤信息非常凌亂,不易定位錯誤

以上就是小編分享的全部內容了,如果覺得不錯還請留下免費的贊和收藏
如果有建議歡迎通過評論區或私信留言,感謝您的大力支持。
一鍵三連好運連連哦~~

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90551.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90551.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90551.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

10BASE-T1S核心機制——PLCA參數詳解

導語&#xff1a; PLCA是10BASE-T1S的核心機制&#xff0c;了解PLCA才能更好地使用10BASE-T1。 本文將通過介紹具體配置&#xff0c;以及實戰例子&#xff0c;帶你掌握PLCA。 以下測試內容使用KUNHONG-U10BT1S-EVB設備測試&#xff0c; 設備符合IEEE 802.3cg標準&#xff0…

uniapp vue apk那邊輸入法遮擋頁面內容

解決辦法&#xff1a;pages.json配置如下{"globalStyle": {"app-plus": {"softinputMode": "adjustResize"}} }效果&#xff1a; 鍵盤彈出時自動調整窗口大小&#xff0c;所有內容上推&#xff08;兼容性最佳&#xff09;文件內容如下…

2507C++,系統服務0與1

原文 窗口上的系統調用通過,每個由系統調用(x64)或sysenter(x86)CPU指令調用的NTDLL.dll,如NTDLL的NtCreateFile的以下輸出所示: 這里 0:000> u ntdll!NtCreateFile: 00007ffcc07fcb50 4c8bd1 mov r10,rcx 00007ffcc07fcb53 b855000000 mov eax,55h…

人工智能冗余:大語言模型為何有時表現不佳(以及我們能做些什么)

像 GPT - 4 這樣的大語言模型&#xff08;LLMs&#xff09;徹底改變了我們與技術交互的方式。它們可以撰寫文章、生成代碼、回答問題&#xff0c;甚至幫助我們構思創意。但任何花時間使用過這些模型的人都知道&#xff0c;它們的輸出有時會讓人感覺……不太對勁。表述冗長、格式…

Cursor替代品亞馬遜出品Kiro下載

Cursor替代品亞馬遜出品Kiro下載 支持Claude Sonnet4.0與3.7 點擊下載 備用鏈接&#xff1a;https://pan.xunlei.com/s/VOW-nBmVgR3ewIIAm7jDsf99A1?pwd6bqu#

MySQL 事務管理

一、前言 CURD 不加控制&#xff0c;會有什么問題&#xff1f; CURD 滿足什么屬性&#xff0c;能解決上述問題&#xff1f; 買票的過程得是原子的。買票應該不能受互相的影響。買完票應該要永久有效。買前和買后都要是確定的狀態。 什么是事務&#xff1f; 事務就是一組 DML 語…

yarn在macOS上的安裝與鏡像源配置:全方位指南

在前端開發領域&#xff0c;高效的包管理工具是提升開發效率的關鍵。yarn 作為一款由 Facebook 推出的包管理器&#xff0c;憑借其快速、可靠、安全的特性&#xff0c;逐漸成為眾多開發者的首選。對于 macOS 用戶而言&#xff0c;正確安裝 yarn 并合理配置鏡像源&#xff0c;能…

Qt 插件架構開發與應用

Qt的插件架構是其模塊化和可擴展性的核心機制之一&#xff0c;它允許開發者通過動態加載插件&#xff08;Plugins&#xff09;擴展應用功能&#xff0c;而無需重新編譯主程序。這種架構廣泛應用于IDE&#xff08;如Qt Creator&#xff09;、媒體播放器&#xff08;解碼器擴展&a…

打破傳統局限:FinOps云成本優化助力企業云成本管理升級

在云計算日益普及的當下,企業紛紛將業務遷移到云端,以期獲得更高效、靈活的IT資源管理方式。然而,云成本管理問題也隨之而來,高額的云支出、資源利用不充分、成本控制難等,成為企業云管理之路上的絆腳石。此時,奇墨科技FinOps云成本優化正以其獨特的優勢,助力企業打破傳統局限,…

HDFS寫性能優化技巧詳解:從理論到實踐

HDFS寫性能優化概述在大數據處理的生態系統中&#xff0c;Hadoop分布式文件系統&#xff08;HDFS&#xff09;作為核心存儲層&#xff0c;其寫性能直接影響著整個數據處理管道的效率。隨著數據規模的指數級增長&#xff0c;企業對HDFS寫入吞吐量和延遲的要求日益嚴苛&#xff0…

基于AutoJawSegment項目的CBCT圖像分割實踐指南

基于AutoJawSegment項目的CBCT圖像分割實踐指南 前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家,覺得好請收藏。點擊跳轉到網站。 1. 項目背景與概述 1.1 CBCT在口腔醫學中的應用 錐形束計算機斷層掃描(Cone Beam Computed Tomograph…

docker部署的ragflow服務遷移 數據卷遷移

[docker ragflow數據遷移]目錄背景一、我的配置文件? 數據存儲路徑一覽&#xff08;基于你的配置文件&#xff09;關于這些 volumes 的說明&#x1f4c1; 如何查看這些卷在本地的具體位置&#xff1f;可能用到的docker 命令如下。&#x1f4e6; 總結建議&#x1f6e0;? 如果想…

Chrome插件學習筆記(三)

Chrome插件學習筆記&#xff08;三&#xff09; 參考文章&#xff1a; https://blog.csdn.net/guoqiankunmiss/article/details/135847091https://blog.csdn.net/guoqiankunmiss/article/details/135974364 1、項目搭建 在前兩篇文章中使用的原生js去操作dom&#xff0c;很費勁…

Android系統中的4KB內存頁簡介

deepseek回答&#xff1a; Android系統中的4KB內存頁是虛擬內存管理的最小單位&#xff0c;其主要用途如下&#xff1a; 一、核心功能 虛擬地址映射 應用程序訪問內存時&#xff0c;系統將虛擬地址按4KB頁框映射到物理內存或磁盤空間&#xff0c;實現進程間的內存隔離和安全訪…

【Chrome】下載chromedriver的地址

下載chromedriver的地址低版本的最新版本的低版本的 http://chromedriver.storage.googleapis.com/index.html 最新版本的 https://googlechromelabs.github.io/chrome-for-testing/#stable

ISP算法——從顏色恒常性到白平衡

前面文章&#xff0c;介紹了人眼感知的物體顏色取決于光源的光譜組成與物體表面的光譜反射特性之間的相互作用。人類視覺的顏色感知是生物機制與認知智能協同作用的結果&#xff0c;人眼視網膜上的視錐細胞檢測光的顏色&#xff0c;視桿細胞分析光的亮度&#xff0c;再共同轉化…

工業缺陷檢測的計算機視覺方法總結

工業缺陷檢測的計算機視覺方法總結 傳統方法 特征提取方式&#xff1a; 顏色&#xff1a;基于HSV/RGB空間分析&#xff0c;如顏色直方圖、顏色矩等紋理&#xff1a;采用LBP、Haar、Gabor濾波器等算子提取紋理模式形狀&#xff1a;基于Hu矩、Zernike矩等數學描述符刻畫幾何特性尺…

js實現宮格布局圖片放大交互動畫

可直接運行代碼 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>五圖交互布局</title><style>* {box-sizing: border-box;margin: 0;padding: 0;}.gallery {display: grid;grid-template-c…

easyexcel流式導出

EasyExcel 支持流式導出&#xff0c;這是它的一個重要特性。流式導出可以有效解決大數據量導出時的內存溢出問題。流式導出的優勢內存友好 &#xff1a;不會一次性將所有數據加載到內存中適合大數據量 &#xff1a;可以處理百萬級甚至更多的數據性能穩定 &#xff1a;內存占用相…

廣州 VR 安全用電技術:工作原理、特性及優勢探析?

&#xff08;一&#xff09;沉浸式學習體驗? 在廣州&#xff0c;VR 用電安全培訓技術給用電安全培訓帶來變革。借助頭戴式顯示設備等硬件&#xff0c;結合 3D 建模和實時渲染技術&#xff0c;打造廣州特色用電場景。員工戴上 VR 設備進入虛擬電力場景&#xff0c;能看到電氣設…