淺談C++ templates 函式模板、類模板以及非型別模板引數
最近打算挑選幾個STL容器做個簡單實現,發現裡面牽涉到不少模板知識。這裡算提前學習一下C++模板的相關知識吧。這次主要學習了什麼是函式模板(這個最簡單),類模板以及非型別模板引數。下面挨個舉例說明。
文章目錄
1. 函式模板
函式模板是最簡答的一個,下面就定義一個返回兩個值中最大者的函式模板:
namespace og {
template<typename T>
inline const T& max(const T& a, const T& b) {
return a > b ? a : b;
}
}
我有個習慣,喜歡用和系統相關庫函式同樣的函式名。那麼,為了避免不必要的麻煩,用上了自己的名稱空間。我比較喜歡og
這兩個字母。
咯,使用這種方式就可以呼叫了…
cout << og::max(100, 123) << endl;
cout << og::max(1.23, 999.9) << endl;
可以在控制檯上看到這樣的內容,
123
999.9
2. 類模板
翻了翻侯捷先生的STL書籍,準備動一下手。那麼,第一步就是需要弄清楚類模板。就算原理不太清楚,至少要能看懂類模板,知道其表達的是什麼意思,從而模仿著學會如何寫類模板。與函式模板一樣,這裡也舉個例子,使用類模板定義一個棧類:
第一步: 宣告一個stack
類,並加上類模板。
namespace og {
template<typename T>
class stack {
public:
void push(const T&);
void pop();
T top() const;
bool empty() const {
return elems.empty();
}
private:
std::vector<T> elems;
};
}//end namespace og
第二步: 實現類成員函式。這裡需要注意,上面的stack
宣告是在.h
檔案中,如果在.cpp
目前的解決方式是,同樣在.h
檔案中將類成員函式實現。實現的內容就是下面這個樣子,
namespace og {
template<typename T>
void stack<T>::push(const T& elem) {
elems.push_back(elem);
}
template<typename T>
void stack<T>::pop() {
if (elems.empty()) {
throw std::out_of_range("stack<T>::pop() - empty stack");
}
elems.pop_back();
}
template<typename T>
T stack<T>::top() const {
if (elems.empty()) {
throw std::out_of_range("stack<T>::top() - empty stack");
}
return elems.back();
}
}//end namespace og
同樣,這裡補充一下——如何使用以上的類模板定義的類。你可以這樣,
try {
og::stack<char> sc;
sc.push('a');
cout << sc.top() << endl;
sc.pop();
sc.pop(); //exception
}
catch (const std::exception& msg) {
cout << msg.what() << endl;
}
之所以加上個異常捕獲,因為我這裡想測試一下throw
有沒有效果。當然,如果不加上try
和catch
,當出現throw
時程式會很不友好的崩潰掉。理論上,你的控制檯應該會打印出,
a
stack::pop() - empty stack
3. 非型別模板引數
這種模板引數,我初次見到時,覺得十分怪異。你們提前感受一下,
og::array<int, 5> ai;
從形式上來看,就是給類模板指定了型別(這個我能理解),可是後面那個5是什麼意思?
在我一番學習之後,我才知道——原來這就是非型別模板引數。同樣,我也定義了一個數組類作為例子:
namespace og {
template<typename T, unsigned int N>
class array {
public:
array();
T& operator[] (unsigned int index);
constexpr unsigned int size() noexcept;
private:
T elems[N];
int length;
};
}//end namespace og
是的,這次模板的宣告變成這樣了template<typename T, unsigned int N>
後面多了一個unsigned int N
。而且讓人奇怪的是非型別模板引數好像還只能是整型
的。
我嘗試了整了一個double N
引數,給我報錯了——告訴我“浮點模板引數是非標準的”。
這個等以後研究清楚了,再補充。那麼,繼續放上以上類宣告的實現部分:
namespace og {
template<typename T, unsigned int N>
array<T, N>::array() : length(N) {
memset(elems, 0, sizeof(T)*N);
}
template<typename T, unsigned int N>
T& array<T, N>::operator[](unsigned int index) {
if (index >= size()) {
throw std::out_of_range("array<T, N>::operator[]() - index out of range");
}
return elems[index];
}
template<typename T, unsigned int N>
constexpr unsigned int array<T, N>::size() noexcept {
return length;
}
}//end namespace og
這裡,主要是實現了一個operator[]
函式,當然也還有其它兩個函式。
咯,再一次看看怎麼用,又和老夥伴見面了——og::array<int, 5> ai;
try {
og::array<int, 5> ai;
ai[0] = 4;
ai[2] = 123;
for (unsigned int i = 0; i < ai.size(); ++i) {
cout << ai[i] << endl;
}
cout << ai[7] << endl; //exception
}
catch (const std::exception& msg) {
cout << msg.what() << endl;
}
最後,再來看看控制檯是什麼樣子?
4
0
123
0
0
array<T, N>::operator - index out of range
到此處還意猶未盡的話,推薦閱讀《對C++ templates類模板的幾點補充(Traits類模板特化)》這篇文章——主要講述了模板的預設引數和類模板特化的相關知識,當然還涉及一些STL迭代器的內容。
2018-12-22 北京 海淀
Reference: C++ Templates(中文版)