C++數據結構模板進階的多方面分析
⭐️博客代碼已上傳至gitee:https://gitee.com/byte-binxin/cpp-class-code
🌏非類型模板參數
模板參數分類類型形參與非類型形參。
- 類型模板形參:出現在模板參數列表中,跟在class或者typename後面的參數類型名稱。(這個我們之前有講過)
- 非類型模板形參:用一個常量作為模板的一個參數,必須是整形傢族中的類型參數,否則不行。他在模板中可以當常量使用。
實例:
// 類型模板參數 namespace wxj { // 非類型模板參數 N 是一個常量參數,隻能是整形傢族的:int short char long long long 自定義類型和其他類型都不能作費類型模板參數 // 必須在編譯期就能確認結果 template<class T, size_t N = 10> class Array { public: Array() :_size(N) {} T& operator[](size_t i) { return _arr[i]; } const T& operator[](size_t i) const { return _arr[i]; } size_t size() { return _size; } bool empty() { return _size == 0; } private: T _arr[N]; size_t _size; }; void TestArray() { Array<int, 5> arr; for (size_t i = 0; i < arr.size(); ++i) { arr[i] = i; } for (size_t i = 0; i < arr.size(); ++i) { cout << arr[i] << " "; } cout << endl; } } int main() { wxj::TestArray(); return 0; }
代碼運行結果如下:
看上面,我們定義瞭一個數組類,空間大小由N決定,類型是Array<T, size_t>
註意:
- 非類型形參必須是整形傢族中的類型,浮點數和類對象都不行。
- 非類型的模板形參必須在編譯期間就能確認結果。
🌏模板的特化
模板特化:在原模板類的基礎上,針對特殊類型所進行的特殊化的實現。分為函數模板特化 和類模板特化。
🍯函數模板的特化
特化的步驟
- 必須先有一個基礎的函數模板
- 關鍵字template後面接一對空的尖括號<>
- 函數名後跟一對尖括號<>,裡面指定需要的特化的類型
- 函數形參列表:必須和函數模板的基礎參數類型完全一致
實例
// 模板的特化 模板的特殊化 template<class T> bool IsEqual(T& left, T& right) { return left == right; } // 特化 針對某些類型進行特殊化處理 template<> bool IsEqual<const char* const>(const char* const& left, const char* const& right) { return strcmp(left, right) == 0; }
註意: 一般情況下如果函數模板遇到不能處理或者處理有誤的類型,為瞭實現簡單通常都是將該函數直接給出。
bool IsEqual(char* left, char* right) { return strcmp(left, right) == 0; }
🍯類模板的特化
類模板的特化分為全特化和偏特化。
全特化: 對類模板參數列表的類型全部都確定(明確指定)
template <class T1, class T2> class Date { public: Date() { cout << "Date<T1, T2>" << endl; } private: T1 _d1; T2 _d2; }; // 全特化 template<> class Date<int, double> { public: Date() { cout << "Date<int, double>" << endl; } private: int _d1; double _d2; };
偏特化: 堆類模板的參數列表中部分參數進行確定化分為部分特化和參數進一步限制
部分特化
// 部分 template<class T2> class Date<int, T2> { public: Date() { cout << "Date<int, T2>" << endl; } private: int _d1; T2 _d2; };
參數進一步限制 如下有T*和T&,是模板的類型轉為指針類型和引用類型
// 參數進一步限制 堆模板參數更進一步的條件限制 template <class T1, class T2> class Date<T1*, T2&> { public: Date(int& a) :_d2(a) { cout << "Date<T1*, T2&>" << endl; } private: T1* _d1; T2& _d2; };
實例 我們試著實例化幾個對象,看他們用的是哪個模板
int main() { Date<int, int> d1; Date<int, double> d2; Date<int, float> d3; int a = 10; Date<int*, int&> d4(a); return 0; }
代碼運行結果:
🌏模板的分離編譯
分離編譯: 我們對這個應該是不陌生的,就是把函數的聲明放在一個叫**.h的文件中,實現都放在一個叫.cpp**的文件中,這樣方便我們管理。
下面我們試著對模板進行分離編譯:
// a.h #pragma once // 普通函數 void Swap(int& a, int& b); // 函數模板 template<class T> T Add(const T& a, const T& b); // a.cpp #define _CRT_SECURE_NO_WARNINGS 1 #include "a.h" // 普通函數 void Swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } // 函數模板 template<class T> T Add(const T& a, const T& b) { return a + b; } // test.cpp #include "a.h" int main() { int a = 3; int b = 4; Swap(a, b); cout << "a = " << a << " b = " << b << endl; cout << Add(a, b) << endl; return 0; }
代碼運行結果如下
代碼運行時發生瞭報錯,說Add這個函數是沒有見過的。得出結論:函數模板不能分離編譯,普通函數可以。
為什麼會這樣呢?
C++程序運行一般經過幾個階段:預處理——>編譯——>匯編——>鏈接(更詳細的內容可以參考往期博客——程序的編譯)
- 模板在.cpp中定義瞭,由於不知道T的類型,所以沒有對模板進行實例化。
- a.h 和 a.cpp 走的是兩條不同的路,兩條路都沒有對模板進行實例化(因為不知道T的類型)。
- 因為沒有對模板進行實例化,所以沒有函數參數,也就沒有函數地址,所以在鏈接時,test.cpp中的調用Add函數時,沒有函數地址,call調用不到Add函數,所以報錯。
解決方法:
- 暴力:不分離編譯,統一放在一個.h或.hpp的文件中
- 模板定義位置顯示實例化(不推薦,這樣就失去瞭泛型的特點)
🌐總結
模板進階也就是這些內容瞭,喜歡的話,歡迎收藏支持~
到此這篇關於C++數據結構模板進階的多方面分析的文章就介紹到這瞭,更多相關C++ 模板進階內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!