本文是《C++標准程序庫》模板相關內容的一個讀書筆記,加上自己的一些理解和實踐。
C++標准程序庫:自修教程與參考手冊 PDF中文版 下載 http://www.linuxidc.com/Linux/2013-07/87264.htm
1.模板
STL中大量使用模板以實現一個通用的工具庫,比如STL提供了很多模板容器類別。模板(template)是為“一個或多個尚未明確的型別”所編寫的函數或類別。使用模板時,可以顯式(explicitly)或隱式(implicitly)地將型別當作參數來傳遞。
有如下模板函數:
template <typename T>
inline const T& max(const T& a, const T& b) {
return a < b ? b : a;
}
可做如下調用:
max<int>(1, 2);
max(2, 3);
第一種方式是顯式將型別作為參數傳遞給模板,第二種方式讓編譯器進行模板參數的自動推導,即模板參數的隱式傳遞。
2.模板的編譯
通常我們將一個類的聲明和定義(實現)分別編寫在.h文件和.cc文件中,但是模板的聲明和實現都必須放在.h文件中才能編譯通過。
首先,要明確編譯單元的概念。一個編譯單元是指一個.cc文件和它所包含的所有.h文件,編譯器將一個編譯單元編譯成一個.o文件。
其次,模板並非一次編譯就生成出適合所有型別的代碼,而是針對被使用的某個(或某組)型別進行編譯。這要求在編譯模板時必須先知道它的某個實例,再針對這個實例編譯出相應型別的模板代碼。因此,如果我們將模板的聲明和實現分別寫在.h和.cc文件中,編譯器在處理模板的.cc文件所代表的編譯單元時,實際上是不會生成目標代碼的,因為編譯器沒有被告知模板的任何一個實例。
有如下分離的模板聲明和實現:
max_temp.h
ifndef MAX_TEMP_H_
#define MAX_TEMP_H_
template <typename T>
const T& max(const T& a, const T& b);
#endif // MAX_TEMP_H_
max_temp.cc
#include "max_temp.h"
template <typename T>
const T& max(const T& a, const T& b) {
return a > b ? a : b;
}
編譯模板並查看生成的.o文件中的符號表:
sw@gentoo ~ $ g++ -c max_temp.cc
sw@gentoo ~ $ nm max_temp.o
sw@gentoo ~ $
可見max_temp.o中的符號表為空,即編譯器並沒有為模板生成任何目標代碼。
如果將模板的聲明和實現都放在.h文件中,並且在使用此模板的.cc文件中包含聲明和實現此模板的.h文件,則編譯器在處理此.cc文件時,將會得到模板的一個或多個實例,並且根據模板.h文件中的模板聲明和實現為模板生成相應型別的代碼。
我們將上述示例代碼作如下更改:
max_temp.h
#ifndef MAX_TEMP_H_
#define MAX_TEMP_H_
template <typename T>
const T& max(const T& a, const T& b);
#include "max_temp-inl.h"
#endif // MAX_TEMP_H_
max_temp-inl.h
#ifndef MAX_TEMP_INL_H_
#define MAX_TEMP_INL_H_
template <typename T>
const T& max(const T& a, const T& b) {
return a > b ? a : b;
}
#endif // MAX_TEMP_INL_H_
main.cc
#include "max_temp.h"
int main(int argc, char* argv[]) {
int m = max(1, 2);
return 0;
}
編譯main.cc並查看生成的.o文件中的符號表:
sw@gentoo ~ $ g++ -c main.cc
sw@gentoo ~ $ nm main.o
00000000 W _Z3maxIiERKT_S2_S2_
00000000 T main
sw@gentoo ~ $
可以看到編譯器為模板生成了目標代碼。
另外,在這個示例中,我們將模板的實現編寫在-inl.h文件中,並且在聲明模板的.h文件的末尾包含相應的-inl.h文件。這樣可以像非模板類型一樣將聲明和實現分離,從而得到更優雅的代碼。