初識C++ · 模板進階

目錄

前言:

1 非類型模板參數

2 按需實例化

3 模板特化

4 模板的分離編譯


前言:

前面模板我們會了簡單的使用,這里帶來模板的進階,當然,也就那么幾個知識點,并不太難。


1 非類型模板參數

先來看這樣一段代碼:

#define N 100
template<class T>
class Arr
{
public:private:T _arr[N];
};

如果我們想要創建一個整型數組,可以使用這個類來創建,但是我們面臨一個問題就是該數組的大小是固定的,我們想要簡單控制這個數組的大小,可以使用宏,但是還是不夠簡便,因為宏不方便調試不說,實際上也是指定了大小,那么我們想要使用一個類,來創建不同大小的數組該怎么辦?
這里使用到的就是非類型模板參數,如下:

template<class T,size_t N = 100>
class Arr
{
public:private:T _arr[N];
};
int main()
{Arr<int,10> a1;Arr<int,1000> a2;Arr<int> a3;return 0;
}

這里就得到了我們想要的不同大小的數組,那么,來個問題,編譯器一共實現了幾個類?

答:編譯器這里一共實現了3個類,編譯器根據模板參數的不同,就實現了不同的類。這里的非類型模板參數,我們可以理解為常量,如這里的N,但是在C++11只支持整型,連浮點數都不可以,只支持整型,比如int size_t char一類的,在C++ 20之后才可以支持其他類型。

這里涉及到了數組,那么引入一個小的知識點就是對于越界來說,array 數組 vector有著不同的反應:

int main()
{int arr[10];arr[10];arr[15] = 1;return 0;
}

對于普通數組來說,普通的越界只讀來說,比如arr[10]是檢查不來錯誤的,越界寫來說,也是很多抽查不出來的,比如這段代碼在vs2019上就不會報錯。

那么對于array來說:

int main()
{std::array<int,10> array;array[10];return 0;
}

任何讀寫越界都會報錯,但是呢,這是c++委員會后面加的,但是挺雞肋的,因為我們有vector。

int main()
{std::vector<int> v;v.reserve(1000);return 0;
}

vector對越界的讀寫都會報錯,這是一方面,其次是array是靜態的數組,也就是大小定了,并且,它所屬的空間是棧,棧的空間相對堆來說就會小很多,所以面臨開大空間的時候,array就不吃香了,vector沒事,因為可以動態開辟。


2 按需實例化

先看這樣一段代碼:

template<class T,size_t N = 100>
class Arr
{
public:T& operator[](size_t i){size(1);return _arr[i];}size_t size(){return _size;}bool empty(){return _size == 0;}
private:T _arr[N];size_t _size = 0;
};
int main()
{Arr<int> a1;a1.empty();return 0;
}

從語法層面來說,size()函數沒有參數,那么我們傳參數的話就會導致報錯對吧?可是,實際上:

代碼是沒有報錯,也就是說size()傳參數是對的嗎?

不,這是因為按需實例化

在主函數里面,我們實例化了a1,并且調用了empty函數,但是我們沒有調用operator[]函數,那么編譯器就不會實例化operator函數,因為我們沒有調用,既然沒有實例化size函數,那么傳什么都不會報錯,這就是按需實例化

再細節一點來說,編譯器會根據模板實例化->實例化一個半成品模板->再實例化為一個具體的類或者函數->最后才是語法編譯,所以沒有語法報錯。


3 模板特化

特化我們可以理解為特殊化處理,比如我們在棧和隊列的時候實現的日期類的比較,就可以不用仿函數來實現比較,可以用特化來處理。

日期類:

class Date
{
public:friend ostream& operator<<(ostream& _cout, const Date& d);Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}
private:size_t _year;	size_t _month;size_t _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}
template<class T>
bool less(const T& a,const T& b)
{return a < b;
}
int main()
{Date d1(2024, 5, 20);Date d2(2024, 5, 21);cout << (d1 < d2) << endl;return 0;
}

比如,使用函數模板,對于重載了比較符號的比較是沒問題的,如果使用指針就會報錯,因為默認是按照指針比較的,這里就可以使用特化:

int main()
{Date* p1 = new Date(2020, 1, 1);Date* p2 = new Date(2020, 1, 2);cout << (p1 < p2) << endl;return 0;
}

這段代碼是有問題的是不用多說的,下面是解決方案:

template<class T>
bool Less(T left, T right)
{cout << "bool Less(T left, T right)" << endl;return left < right;
}template<>
bool Less<Date*>(Date* p1,Date* p2)
{return *p1 < *p2;
}int main()
{Date d1(2022, 7, 7);Date d2(2022, 7, 8);Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl;return 0;
}

這里的語法就比較怪了,現在使用的是函數模板,有點像函數重載的感覺,當然,我們也可以直接重載一個出來:

bool Less(Date* left, Date* right)
{return *left < *right;
}

那么,調用是怎么調用的呢?

這里就和半成品,成品是一個道理,重載的函數就相當于成品,特化的函數快成品了,模板就是個半成品,調用的順序也就說的通了。

以上是函數模板的特化,看起來就像是函數重載,接著是類中的模板特化:


//普通
template<class T1,class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }};//全特化
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; }
};

使用方式和函數模板其實是差不多的,在特化這里分為全特化和偏特化,特化也不是什么特別的東西,其實就是對參數的進一步限制而已。


4 模板的分離編譯

使用模板的時候,定義和聲明最好放在一個文件,.h和.c文件分離會報錯的,這里簡單舉個例子:

// a.h
template<class T>
T Add(const T& left, const T& right);//a.cpp
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}

在調用這個函數的時候就會報錯,只需要想清楚一個簡單的問題就可以了,兩個T是不是一樣的T,能否用.h文件里面的T去平替.cpp里面的T,當然是不可以的,所以這里,就會報錯,報的是鏈接錯誤,.h文件編譯成功后,.cpp里面的文件是沒有編譯好的,因為T不知道是什么類型,調用的時候就會報錯。

在鏈接階段,編譯器按照修飾好之后的函數名在符號表里面尋找函數,不知道類型就沒有生成修飾好的函數名,那么就會報如下類似的錯:


感謝閱讀!

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

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

相關文章

嵌入式移植jpeglib--Linux交叉編譯ARM平臺

一 、交叉編譯jpeg庫 1.下載源碼tar.gz 2. 源碼目錄下執行 jpeglib配置文件 ./configure CCarm-none-linux-gnueabihf-gcc LDarm-none-linux-gnueabihf-ld --prefix/work/jpeg_arm_lib --exec-prefix/work/jpeg_arm_lib --enable-shared --enable-static --hostarm-none-linu…

經典文獻閱讀之--MGS-SLAM(單目稀疏跟蹤和高斯映射與深度平滑正則化)

Tip: 如果你在進行深度學習、自動駕駛、模型推理、微調或AI繪畫出圖等任務&#xff0c;并且需要GPU資源&#xff0c;可以考慮使用UCloud云計算旗下的Compshare的GPU算力云平臺。他們提供高性價比的4090 GPU&#xff0c;按時收費每卡2.6元&#xff0c;月卡只需要1.7元每小時&…

CiteScore 2023發布,AI Open斬獲45分,位列全球計算機領域前1%

與影響因子&#xff08;IF&#xff09;一樣&#xff0c;引用分數&#xff08;CiteScore&#xff09;同樣是衡量學術期刊影響力的重要指標之一&#xff0c;且大有趕超前者的勢頭。 6 月 6 日&#xff0c;CiteScore 2023 正式發布&#xff0c;人工智能領域可自由訪問的期刊平臺 …

Java 8 中的 Stream API,用于處理集合數據

Java 8 引入了 Stream API&#xff0c;使得處理集合數據變得更加簡潔和高效。Stream API 允許開發者以聲明式編程風格操作數據集合&#xff0c;而不是使用傳統的迭代和條件語句。 一、基本概念 1.1 什么是 Stream Stream 是 Java 8 中的一個新抽象&#xff0c;它允許對集合數…

CSRF 令牌的生成過程和檢查過程

在 Django 中,CSRF 令牌的生成和檢查過程是通過 Django 的 CSRF 中間件 (CsrfViewMiddleware) 和模板標簽 ({% csrf_token %}) 自動處理的。以下是詳細的生成和檢查過程: CSRF 令牌的生成過程 用戶訪問頁面: 當用戶第一次訪問頁面時,Django 會為用戶創建一個會話。如果用戶…

人工智能、深度學習和機器學習的前世今生

人工智能、深度學習和機器學習的前世今生 引言 在當今科技飛速發展的時代&#xff0c;人工智能&#xff08;AI&#xff09;、機器學習&#xff08;ML&#xff09;和深度學習&#xff08;DL&#xff09;已經成為引領第四次工業革命的重要力量。這些技術不僅在學術界和工業界掀…

C++ 數據共享與保護學習記錄【代碼】

一.項目一 1.頭文件.h //A.h #pragma once //防止頭文件被重復包含&#xff08;重復包含會被重復編譯&#xff0c;也就是該類會被重復定義&#xff09; #ifndef HEAD_H //等價于&#xff08; #if !defined(HEAD_H) ) //defined是一個預處理操作符&#xff0c;相當于一個表達式…

整理好了!2024年最常見 20 道分布式、微服務面試題(二)

上一篇地址&#xff1a;整理好了&#xff01;2024年最常見 20 道分布式、微服務面試題&#xff08;一&#xff09;-CSDN博客 三、請解釋CAP定理及其含義。 CAP定理是分布式計算領域的一個基本概念&#xff0c;由計算機科學家Eric Brewer在2000年提出&#xff0c;并由科學家Se…

力扣76.最小覆蓋子串

力扣76.最小覆蓋子串 用哈希表記錄每個字母出現次數 枚舉右端點 判斷是否能全覆蓋如果可以 并且更短 就更新 j 縮小區間再判斷 class Solution {bool is_covered(int cnt_s[], int cnt_t[]) {for (int i A; i < Z; i) {if (cnt_s[i] < cnt_t[i]) {return false;}}fo…

上網操作的必要條件

一、 網卡 1、 為什么需要網卡 計算機為了實現網絡通信&#xff0c;必須都要有網卡這個東西&#xff0c;網卡是計算機眾多外部設備之一&#xff08;其它還有硬盤、鍵盤等&#xff09;&#xff0c;計算機將數據發給網卡&#xff0c;網卡負責將數據往外發送&#xff0c;通過IP定…

技術團隊的沖突管理: 谷歌亞里士多德項目的啟示

有效的沖突管理對于技術團隊保持高效和創新的工作環境至關重要。谷歌的亞里士多德項目是一項內部研究&#xff0c;旨在了解成功團隊的因素&#xff0c;強調了心理安全和開放溝通在促進團隊成員之間的合作和解決分歧方面的重要性。本文將探討受谷歌的亞里士多德項目和其他數據點…

工廠生產計劃難以執行的真正原因及對策

在制造業中&#xff0c;生產計劃的執行對于企業的運營至關重要。然而&#xff0c;許多工廠在生產計劃執行過程中面臨著諸多挑戰&#xff0c;尤其是物料齊套率低的問題。本文將探討工廠生產計劃難以執行的真正原因&#xff0c;并提出相應的解決對策。 一、生產計劃難以執行的真…

mysql optimizer_switch : 查詢優化器優化策略深入解析

碼到三十五 &#xff1a; 個人主頁 在 MySQL 數據庫中&#xff0c;查詢優化器是一個至關重要的組件&#xff0c;它負責確定執行 SQL 查詢的最有效方法。為了提供DBA和開發者更多的靈活性和控制權&#xff0c;MySQL 引入了 optimizer_switch 系統變量。這個強大的工具允許用戶開…

nginx配置WebSocket參數wss連接

目錄 一、原文連接 二、 配置參數 三、實踐 四、重啟nginx 五、連接websocket 一、原文連接 nginx配置websocket支持wss-騰訊云開發者社區-騰訊云 二、 配置參數 map $http_upgrade $connection_upgrade { default upgrade; close; } upstream websocket { se…

聚類的外部指標(Purity, ARI, NMI, ACC) 和內部指標(NCC,Entropy,Compactness,Silhouette Index)

在聚類分析中,外部指標和內部指標用于評估聚類結果的質量。外部指標需要知道真實的類別標簽,而內部指標則僅基于聚類結果本身進行評估。 外部指標 Purity (純度): 計算聚類結果中每個簇中最多數目的樣本所屬的類別,并計算所有簇的該類別樣本數之和占所有樣本數的比例。 Pyt…

【操作系統】進程與線程的區別及總結(非常非常重要,面試必考題,其它文章可以不看,但這篇文章最后的總結你必須要看,滿滿的全是干貨......)

目錄 一、 進程1.1 PID(進程標識符)1.2 內存指針1.3 文件描述符表1.4 狀態1.5 優先級1.6 記賬信息1.7 上下文 二、線程三、總結&#xff1a;進程和線程之間的區別&#xff08;非常非常非常重要&#xff0c;面試必考題&#xff09; 一、 進程 簡單來介紹一下什么是進程&#xf…

win 系統 cmd 命令從私庫上傳,下載jar包

1. 確保maven環境變量或者maven安裝無誤&#xff1b; 2.私庫下載 命令 mvn dependency:get -DgroupId<your_group_id> -DartifactId<your_artifact_id> -Dversion<your_version> -Dpackagingjar -Dfile<path_to_your_jar_file> -Durl<your_privat…

寫入文件內容

自學python如何成為大佬(目錄):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在實例01中&#xff0c;雖然創建并打開一個文件&#xff0c;但是該文件中并沒有任何內容&#xff0c;它的大小是0KB。Python的文件對象提供了write()…

【電路筆記】-分貝

分貝 分貝是以 10 為底的對數比,用于表示電路中功率、電壓或電流的增加或減少。 1、概述 一般來說,分貝是響度的度量。 在設計或使用放大器和濾波器電路時,計算中使用的一些數字可能非常大或非常小。 例如,如果我們將兩個放大器級級聯在一起,功率或電壓增益分別為 20 和…

os和os.path模塊

自學python如何成為大佬(目錄):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 目錄也稱文件夾&#xff0c;用于分層保存文件。通過目錄可以分門別類地存放文件。我們也可以通過目錄快速找到想要的文件。在Python中&#xff0c;并…