1. 程式人生 > >模板函式的宣告和定義為何不能分開放在兩個檔案中?

模板函式的宣告和定義為何不能分開放在兩個檔案中?

首先明確:

對普通函式來說,宣告放在標頭檔案中,定義放在原始檔中,其它的地方要使用該函式時,僅需要包含標頭檔案即可,因為編譯器編譯時是以一個原始檔作為單元編譯的,當它遇到不在本檔案中定義的函式時,若能夠找到其宣告,則會將此符號放在本編譯單元的外部符號表中,連結的時候自然就可以找到該符號的定義了。

而對模板函式來說,首先明確,模板函式是在編譯器遇到使用模板的程式碼時才將模板函式例項化的。若將模板函式宣告放在tem.h,模板定義放在tem.cpp,在main.cpp中包含標頭檔案,呼叫add,按道理說應該例項化int add(int,int)函式,即生成add函式的相應程式碼,但是此時僅有宣告,找不到定義,因此此時,它只會例項化函式的符號,並不會例項化函式的實現,即這個時候,在main.o編譯單元內,它只是將add函式作為一個外部符號,這就是與普通函式的區別,對普通函式來說,此時的add函式已經由編譯器生成相應的程式碼了,而對模板函式來說,此時並沒有生成add函式對應的程式碼

。此時編譯main.cpp單元不會報錯,但連結就會出現add函式未定義的錯誤。

因此,我們可以通過顯式的例項化定義,即通過加上語句temmplate int add(int,int),編譯器看到此語句將會生成add方法的int版本,這樣的話,再連結就不會報錯了。此外,這樣做通常也能夠提高編譯的效率。試想,如果在tem.h檔案內定義模板,假如有三個原始檔均包含了該標頭檔案且均使用了模板(假定均呼叫了add模板的int版本),則在這三個原始檔內必然都會生成add函式的例項。顯然效率不高。而如果像上面那樣使用該模板,則只會在tem.cpp檔案中例項化。

最後,對於類模板來說,也同樣符合上面的原則。在實際類模板的例項化時,實際上是分幾步的,首先當然是類模板的例項化,然後還有類成員函式的例項化,我們知道在類的定義中,其實只是聲明瞭類的成員函式,編譯器實際上是把類的成員函式編譯成修改名稱後的全域性函式的,因此在使用類模板的時候,首先會初始化類模板,同時初始化類模板相應的建構函式,使用類模板的例項呼叫相應的成員函式時,才會初始化類模板的成員函式。如果類模板的成員函式的定義與類的定義不在同一個編譯單元中(分離式編譯),此時呼叫類的成員函式便會出現未定義的錯誤。而當我們像程式碼中那樣在某個地方顯式的呼叫它的時就不會出現此類問題了。

總結:其實很明顯,明確一點就可以了,即編譯器只要遇到使用模板函式時就會例項化相應的函式,若在此編譯單元內沒有模板函式的定義,它當然不能夠例項化成功了。因此通常情況下模板函式的宣告與定義均放在同一檔案內,因此這樣就保證了在使用模板的地方一定可以例項化成功了。同時,由編譯器保證只生成某種型別的一個例項版本,不用擔心重複例項化的問題。

c++primer上面只說了類模板的成員函式可以不在標頭檔案中定義,卻始終感覺說得不清不楚,因為實際上像普通類那樣類的定義與實現放在不同的檔案中的話,是會連結出錯的。總之,若你不想出現任何未定的錯誤,將類模板或函式模板的定義與宣告放在同一個檔案中就行了。