現在的位置: 首頁 > 黃專家專欄 > 正文

C++模板顯式實例化

2014年10月30日 黃專家專欄 ⁄ 共 1463字 ⁄ 字型大小 評論關閉

討論模板的顯式實例化之前,先弄清楚什麼是模板的實例化。

模板的實例化 : 指的是用具體的類型替代了模板參數的一個過程。這個過程的產物是一個模板的實例

另外,只要在代碼中正確使用模板,編譯器會自動引發一個模板的實例化過程,這叫做隱式的(implicit)實例化。

例如:

1
2
3
4
5
6
7
8
9
10
11
#include<string>
#include<map>

template<class T>
void Foo(T t) {
}

int main() {
  Foo(10);
  std::map<std::string> m;
}

隱式的(implicit)實例化在使用模板時會自動地在當前代碼單元之前插入模板的實例化代碼。需要知道的是,在使用模板的地方,編譯器通常需要訪問模板和某些(如果需要的話)模板成員的定義,如果僅僅只有模板的聲明,是不足夠的。這也就是往往將整個模板聲明和實現寫在一個很大的頭文件中的緣故。

幾種主流的編譯器對模板支持的一些實現:

Borland 模型

在每一個編譯單元,編譯器生成遇到的所有的模板的實例,而且鏈接器知道,特定的模板實例,可以在多個目標文件和程序庫裡面出現,那麼編譯器會標記他們,當鏈接器發現多個實例的時候,它合併他們。

這種模型的好處是顯而易見的,鏈接器只需要考慮單個編譯單元,因為所有的實例化的定義,都在目標文件中存在,每一個目標文件都包含著針對所有可以鏈接的模板定義代碼,這降低了複雜性。

但是,缺點也是明顯的,編譯器生成多個模板實例,鏈接器合併它們,這無疑增加了編譯時間,而且所有目標文件的大小總和可能會更大。

另一個不太明顯的缺點是,鏈接器可能不會檢查兩個實例化體是否是一樣的,因為在生成的代碼中,同一個模板特化的多個實例中可能會出現一些細微的差距(C++ Template The Complete Guide 10.4.1)。

Cfront 模型

使用一個中心庫,它存儲了模板的實例化位置和模板的實例。編譯器把遇到的模板定義與當前可生成的模板實例存放到模板倉庫中。在鏈接階段,鏈接器調用編譯器生成這些模板實例,這樣保證了所有相同模板實例化體的代碼都是一樣的,並且大大加快了編譯時間。這種模式的缺點是大大增加了複雜性。

現在說回到顯式實例化

顯式實例化需要顯式實例化指示符(explicit instantiation derective)。

1
2
3
template<class T>

template class Foo<int>;  // 基於 int 的顯式實例化

但是,需要注意的是,對於在前面已經實例化的成員,你不能對它再一次實例化。

使用 -fno-implicit-templates 編譯選項指定禁止隱式的模板實例化,所有模板示例都可以顯式寫出來。

這樣的有點事實例化可以在需要的時候才進行,避免包含龐大頭文件的開銷,更可以把模板定義和聲明分開。

加上 -fno-implicit-templates 編譯選項,這樣的代碼就會出現鏈接錯誤,因為根本找不到模板實例。

1
2
3
4
5
6
7
8
9
#include<map>
// 必須加上顯式實例化指示符
// template class std::_Rb_tree<int, std::pair<int const, int>, std::_Select1st<std::pair<int const, int> >, \
// std::less<int>, std::allocator<std::pair<int const, int> > >;
// template class std::map<int, int>;

int main() {
  std::map<int, int> m;
}

抱歉!評論已關閉.