2009-12-14 71 views
3

如何從虛擬模板類繼承,在此代碼繼承:從在C虛擬模板類++

// test.h 
class Base { 
public: 
    virtual std::string Foo() = 0; 
    virtual std::string Bar() = 0; 
}; 

template <typename T> 
class Derived : public Base { 
public: 
    Derived(const T& data) : data_(data) { } 

    virtual std::string Foo(); 
    virtual std::string Bar(); 

    T data() { 
    return data_; 
    } 

private: 
    T data_; 
}; 


typedef Derived<std::string> DStr; 
typedef Derived<int> DInt; 

// test.cpp 
template<typename T> 
std::string Derived<T>::Foo() { ... } 
template<typename T> 
std::string Derived<T>::Bar() { ... } 

當我嘗試使用DSTR或力,鏈接器抱怨說,有無法解析的外部,它們分別是Derived<std::string>::Foo()Derived<std::string>::Bar(),以及Derived<int>

我在代碼中錯過了什麼嗎?編輯: 謝謝大家。現在很清楚。

+2

一個好的做法是將模板的實現放在同一個頭文件中。 AFAIK :) – AraK 2009-12-14 19:53:39

+0

在許多編譯器中,這不僅僅是良好的做法,而且是必需的。 – Joe 2009-12-14 20:16:18

回答

6

您需要在頭文件中定義template<typename T> std::string Derived<T>::Foo() { ... }template<typename T> std::string Derived<T>::Bar() { ... }。當編譯器正在編譯test.cpp時,它不知道可能在程序的其他部分使用的所有可能的值T

我認爲有一些編譯器在編譯和鏈接步驟之間有聯繫,它們會注意到缺少模板實例的引用,並從聲明它們的.cpp文件中實例化它們。但我不知道它們是哪一個,功能非常罕見。

如果您在頭文件中定義它們,大多數編譯器會將它們作爲一個'弱'符號發送到它們被引用的每個編譯單元中。除了弱符號的一個定義外,鏈接器會拋出所有內容。這會導致額外的編譯時間。

另外,還有一些語法用於顯式實例化模板並強制編譯器在此處發出定義。但是這要求你意識到所有值T可能會有,你將不可避免地錯過一些。

3

這與推導沒有多大關係。這只是模板的一個通用規則:對於大多數編譯器(除了Comeau以外的任何其他編譯器),必須將模板的完整實現放置在每個可以實例化該模板的翻譯單元中 - 通常位於標題中。

即使有了Comeau,您也必須使用export關鍵字來使事情正常工作。因爲他們是唯一實施0​​的人,所以你可能不太在乎。

6

你必須確保成員函數實例化所有需要的類型。

通常這是通過在聲明頭文件中內聯定義模板函數來完成的,因此任何對函數的使用都會導致它們被實例化。

作爲替代方案,您可以在定義它們的源文件中使用顯式實例,但這確實需要您事先知道您的模板將被實例化的所有類型。