2010-04-29 81 views
10

看到下面的代碼,請清除我的疑惑。顯式模板實例如何影響鏈接器可以找到的內容?

  1. 由於ABC是一個模板,它爲什麼不顯示一個錯誤,當我們把TEST.CPP的ABC類的成員函數的定義是什麼?

  2. 如果我把test.cpp代碼放在test.h和remve 2中,那麼它工作正常。爲什麼?

// test.h 
template <typename T> 
class ABC { 
public: 
    void foo(T&); 
    void bar(T&); 
}; 
// test.cpp 
template <typename T> 
void ABC<T>::foo(T&) {} // definition 
template <typename T> 
void ABC<T>::bar(T&) {} // definition 

template void ABC<char>::foo(char &); // 1 
template class ABC<char>;    // 2 
// main.cpp 
#include "test.h" 
int main() { 
    ABC<char> a; 
    a.foo();  // valid with 1 or 2 
    a.bar();  // link error if only 1, valid with 2 
} 
+0

你爲什麼要問?他們是兩個毫不相關的陳述。 – 2010-04-29 07:29:14

+0

@Dennis Zickefoose:大家都是初學者在某些點 – ereOn 2010-04-29 07:33:24

+0

@ereOn:是的,但爲什麼他問能走很長的路向建議適當的解決辦法回答他的問題時採取。 – 2010-04-29 07:57:27

回答

13

在這兩種情況下,你都在做一個明確的實例化。在第二種情況下,只有ABC<char>::foo正在實例化,而在第一種情況下,ABC<char>::bar也正在實例化。

不同的類似的例子可以澄清的含義:

// test.h 
template <typename T> 
class ABC { 
public: 
    void foo(T&); 
    void bar(T&); 
}; 
// test.cpp 
template <typename T> 
void ABC<T>::foo(T&) {} // definition 
template <typename T> 
void ABC<T>::bar(T&) {} // definition 

template void ABC<char>::foo(char &); // 1 
template class ABC<char>;    // 2 
// main.cpp 
#include "test.h" 
int main() { 
    ABC<char> a; 
    a.foo();  // valid with 1 or 2 
    a.bar();  // link error if only 1, valid with 2 
} 

在該示例中,在main編譯器不能看到foo也不bar定義,因此它不能實例化的方法。編譯器在處理main.cpp時會很樂意接受主代碼,因爲您告訴它是一個模板,並且它具有這兩個函數,並且會假定它們將在其他某個翻譯單元中定義。

在包含test.cpp的翻譯單元中,編譯器看到兩個方法定義,並且可以完全處理兩個實例(方法/類)。如果僅存在方法實例化([1]),則編譯器將僅生成該方法,並且會使bar未定義。因此,包含test.h的任何代碼都會與編譯的test.cpp鏈接,並且只使用foo方法進行編譯和鏈接,但bar的使用將由於未定義而無法鏈接。

顯式實例化類模板爲所有成員方法生成符號,在這種情況下,包含test.h和任何包含編譯的test.cpp對象文件鏈接的翻譯單元都將編譯和鏈接。

+0

我有兩個疑惑 1.爲ABC是模板,爲什麼它沒有顯示錯誤,當我們把ABC類的成員函數的界定及在TEST.CPP 2.如果我在test.h把TEST.CPP代碼並刪除2,那麼它的工作好的,請你解釋一下 – Suri 2010-04-29 08:47:24

+0

模板方法定義不需要在類體內部,即使是推薦的。與常規函數一樣,如果編譯器只能看到方法聲明,它會假定它是在其他地方定義的,如cpp中的示例所示。這種方法存在問題,因爲您正在將可用於模板的類型限制爲您爲其提供明確專業化的類型。這可以用於加快編譯時間,對於那些預先知道實例化模板的所有類型的模板化代碼。 – 2010-04-29 09:50:16

+0

第二個問題,如果你把所有的代碼放在一起,編譯器會看到模板定義(方法),所以當你在main中調用'bar'方法時,它會執行該方法的隱式實例化。請注意,只有在編譯器不能隱式實例化模板的情況下才需要顯式實例化,並且在大多數情況下,如果編譯器有足夠的信息,通過允許它決定實例化哪些方法(僅使用那些方法),您將提高編譯時間(和中間值目標文件,即使在大多數情況下,連接器可以丟棄未使用的符號) – 2010-04-29 09:53:19

0

(這是我原來的答覆,由大衛·羅德里格斯的觀察提示的編輯版本。)

#1實例化類模板,並作爲該部分實例的所有方法。

#2實例化類的一個成員方法。作爲它的一部分,它必須實例化類模板,但不是所有其他方法。

如果您在bar()中引入了一個依賴於類型的錯誤(例如,像void *x = b;這樣的語句),則可以看到差異。您將收到#1的編譯器錯誤,但不會與#2。另請注意,編譯器(gcc至少)不會編譯#1,然後是#2,但會編譯其中任何一個,或者如果#2後跟#1

+0

當實例化類模板時,只有實際_used_成員被實例化。這就是爲什麼我相信你#2是錯誤的。大衛的回答也是如此。 – sbi 2010-04-29 07:51:21

0

我想你想{}而不是;在#1。

相關問題