工作積累之模板類例項化

2021-06-25 10:59:03 字數 3545 閱讀 1836

模板函式在宣告的時候, 其實並不存在,函式位址也就無從談起了,而匯出到動態鏈結庫的函式都需要有位址

(動態鏈結庫不能將模板類匯出,因為沒法生成例項)

如果把模板類的宣告和定義都放在標頭檔案中。需要用到模板類的時候,只需要包含該標頭檔案,然後進行例項化。

如果模板類的宣告和定義分別放在標頭檔案和原始檔中。當例項化的時候,只包含標頭檔案會發證鏈結錯誤。

原因是模板類的例項化要分成兩個步驟,模板的特例化和特例的實力化。

編譯器在編譯階段,由於沒有定義,所以編譯器不能生成具體的模板特例,但是這並不會報錯誤,編譯器將把問題拋給鏈結器來做。在編譯原始檔的時候,程式找不到該模板的特例,只是有模板而已,所以無法生成物件。所以會發生錯誤。

以下**牛人blog

***************==

前言常遇到詢問使用模板到底是否容易的問題,我的回答是:「模板的使用是容易的,但組織編寫卻不容易」。看看我們幾乎每天都能遇到的模板類吧,如stl, atl, wtl, 以及boost的模板類,都能體會到這樣的滋味:介面簡單,操作複雜。

本文物件是那些熟悉模板但還沒有很多編寫模板經驗的程式設計師。本文只涉及模板類,未涉及模板函式。但論述的原則對於二者是一樣的。

問題的產生

通過下例來說明問題。例如在array.h檔案中有模板類array:

// array.h

template

class array

;t& operator(int i)

const t& get_elem (int i) const

void set_elem(int i, const t& value)

operator t*()

};然後在main.cpp檔案中的主函式中使用上述模板:

// main.cpp

#include "array.h"

int main(void)

這時編譯和執行都是正常的。程式先建立乙個含有50個整數的陣列,然後設定陣列的第乙個元素值為2,再讀取第乙個元素值,最後將指標指向陣列起點。

但如果用傳統程式設計方式來編寫會發生什麼事呢?我們來看看:

將array.h檔案**成為array.h和array.cpp二個檔案(main.cpp保持不變)

// array.h

template

class array

;t& operator(int i);

const t& get_elem (int i) const;

void set_elem(int i, const t& value);

operator t*();

};// array.cpp

#include "array.h"

templatet& array::operator (int i)

templateconst t& array::get_elem(int i) const

templatevoid array::set_elem(int i, const t& value)

templatearray::operator t*()

編譯時會出現3個錯誤。問題出來了:

為什麼錯誤都出現在第乙個地方?

為什麼只有3個鏈結出錯?array.cpp中有4個成員函式。

要回答上面的問題,就要深入了解模板的例項化過程。

模板例項化

程式設計師在使用模板類時最常犯的錯誤是將模板類視為某種資料型別。所謂型別參量化(parameterized types)這樣的術語導致了這種誤解。模板當然不是資料型別,模板就是模板,恰如其名:

編譯器使用模板,通過更換模板引數來建立資料型別。這個過程就是模板例項化(instantiation)。

從模板類建立得到的型別稱之為特例(specialization)。

模板例項化取決於編譯器能夠找到可用**來建立特例(稱之為例項化要素,

point of instantiation)。

要建立特例,編譯器不但要看到模板的宣告,還要看到模板的定義。

模板例項化過程是遲鈍的,即只能用函式的定義來實現例項化。

再回頭看上面的例子,可以知道array是乙個模板,array是乙個模板例項 - 乙個型別。從array建立array的過程就是例項化過程。例項化要素體現在main.cpp檔案中。如果按照傳統方式,編譯器在array.h檔案中看到了模板的宣告,但沒有模板的定義,這樣編譯器就不能建立型別array。但這時並不出錯,因為編譯器認為模板定義在其它檔案中,就把問題留給鏈結程式處理。

現在,編譯array.cpp時會發生什麼問題呢?編譯器可以解析模板定義並檢查語法,但不能生成成員函式的**。它無法生成**,因為要生成**,需要知道模板引數,即需要乙個型別,而不是模板本身。

這樣,鏈結程式在main.cpp 或 array.cpp中都找不到array的定義,於是報出無定義成員的錯誤。

至此,我們回答了第乙個問題。但還有第二個問題,在array.cpp中有4個成員函式,鏈結器為什麼只報了3個錯誤?回答是:例項化的惰性導致這種現象。在main.cpp中還沒有用上operator,編譯器還沒有例項化它的定義。

解決方法

認識了問題,就能夠解決問題:

在例項化要素中讓編譯器看到模板定義。

用另外的檔案來顯式地例項化型別,這樣鏈結器就能看到該型別。

使用export關鍵字。

前二種方法通常稱為包含模式,第三種方法則稱為分離模式。

第一種方法意味著在使用模板的轉換檔案中不但要包含模板宣告檔案,還要包含模板定義檔案。在上例中,就是第乙個示例,在array.h中用行內函式定義了所有的成員函式。或者在main.cpp檔案中也包含進array.cpp檔案。這樣編譯器就能看到模板的宣告和定義,並由此生成array例項。這樣做的缺點是編譯檔案會變得很大,顯然要降低編譯和鏈結速度。

第二種方法,通過顯式的模板例項化得到型別。最好將所有的顯式例項化過程安放在另外的檔案中。在本例中,可以建立乙個新檔案templateinstantiations.cpp:

// templateinstantiations.cpp

#include "array.cpp"

template class array ; // 顯式例項化

array型別不是在main.cpp中產生,而是在templateinstantiations.cpp中產生。這樣鏈結器就能夠找到它的定義。用這種方法,不會產生巨大的標頭檔案,加快編譯速度。而且標頭檔案本身也顯得更加「乾淨」和更具有可讀性。但這個方法不能得到惰性例項化的好處,即它將顯式地生成所有的成員函式。另外還要維護templateinstantiations.cpp檔案。

第三種方法是在模板定義中使用export關鍵字,剩下的事就讓編譯器去自行處理了。當我在

stroustrup的書中讀到export時,感到非常興奮。但很快就發現vc 6.0不支援它,後來又發現根本沒有編譯器能夠支援這個關鍵字(第乙個支援它的編譯器要在2023年底才問世)。自那以後,我閱讀了不少關於export的文章,了解到它幾乎不能解決用包含模式能夠解決的問題。欲知更多的export關鍵字,建議讀讀herb sutter撰寫的文章。

結論要開發模板庫,就要知道模板類不是所謂的"原始型別",要用其它的程式設計思路。本文目的不是要嚇唬那些想進行模板程式設計的程式設計師。恰恰相反,是要提醒他們避免犯下開始模板程式設計時都會出現的錯誤。

ue4 模板類例項化 關於模板類例項化

模板函式在宣告的時候,其實並不存在,函式位址也就無從談起了,而匯出到 動態鏈結庫不能將模板類匯出,因為沒法生成例項 如果把模板類的宣告和定義都放在標頭檔案中。需要用到模板類的時候,只需要包 含該標頭檔案,然後進行例項化。如果模板類的宣告和定義分別放在標頭檔案和原始檔中。當例項化的時候,只包含標頭檔案...

template模板及模板類的例項化

通常,當我們呼叫乙個函式時,編譯器只需要掌握函式的宣告。類似的,當我們使用乙個類型別的物件時,類定義必須是可用的,但成員函式的定義不必已經出現。因此我們將類定義和函式宣告放在標頭檔案中,而普通函式和類的成員函式的定義放在原始檔中。模板則不同 為了生成乙個例項化版本,編譯器需要掌握函式模板或類模板成員...

C 之模板例項化

模板可以分為類模板與函式模板,它們的宣告形式分別為 templateclass 類名 template返回值型別 函式名 形參表 其中typename後跟的是型別引數,可以是內建型別,也可以是自定義型別,像size這種為非型別引數,為固定值。模板在沒有被例項化的情況下是不會生成二進位制 的,其實例化...