2008-10-16 60 views
2

我試圖創建一個C++類,與模板超類。我的想法是,我可以很容易地從許多具有類似特徵的超類中創建很多類似的子類。模板超類鏈接問題

我已經蒸出有問題的代碼,如下所示:

template_test.h

template<class BaseClass> 
class Templated : public BaseClass 
    { 
public: 
    Templated(int a); 
    virtual int Foo(); 
    }; 

class Base 
    { 
protected: 
    Base(int a); 
public: 
    virtual int Foo() = 0; 
protected: 
    int b; 
    }; 

template_test.cpp

#include "template_test.h" 

Base::Base(int a) 
    : b(a+1) 
    { 
    } 

template<class BaseClass> 
Templated<BaseClass>::Templated(int a) 
    : BaseClass(a) 
    { 
    } 

template<class BaseClass> 
int Templated<BaseClass>::Foo() 
    { 
    return this->b; 
    } 

main.cpp

#include "template_test.h" 

int main() 
    { 
    Templated<Base> test(1); 
    return test.Foo(); 
    } 

當我構建代碼時,出現鏈接器錯誤,說找不到符號Templated<Base>::Templated(int)Templated<Base>::Foo()

快速谷歌認爲,加入以下main.cpp就能解決問題:

template<> Templated<Base>::Templated(int a); 
template<> int Templated<Base>::Foo(); 

但這並不解決問題。將行添加到main.cpp也不起作用。 (不過,有趣的是,把它們添加到都給出了「乘法定義符號」從鏈接錯誤,所以他們必須做什麼......)

然而,把所有的代碼在一個源文件不解決問題。雖然這對於上面的noddy例子來說是可以的,但是如果我被迫將整個文件放在一個cpp文件中,那麼我所看到的真正的應用程序將非常快速地變得難以管理。

有誰知道我在做甚麼可能嗎? (如何)我可以解決我的鏈接器錯誤?

我會假設我可以在class Templated內聯所有的方法,這將工作,但這似乎並不理想。

回答

4

對於使用模板的類,必須爲每個使用它的翻譯單元提供定義。這些定義可以放在一個單獨的文件中,通常是.inl.tcc擴展名;頭文件#include是該文件的底部。因此,即使它在單獨的文件中,對於每個翻譯單元仍然是#included;它不能是獨立的。

所以,你的榜樣,重命名template_test.cpptemplate_test.inl(或template_test.tcc,或其他),則有#include "template_test.inl"(或其他)在template_test.h底部,只是包括後衛的#endif之前。

希望這會有所幫助!

+0

Chris,也許你可以添加澄清,模板實現不能進入單獨的編譯單元(= cpp文件)。 – 2008-10-16 08:09:45

+0

不要忘記刪除顯式實例,讓編譯器自動處理它。 此外,對BaseClass :: b的調用可以用this-> b來替換。 – bltxd 2008-10-16 08:14:16

1

C++ FAQ-lite covers this,以及它的幾種方法。

您不必使所有方法都「內聯」,但是您應該在template_test.h中定義方法體,而不是在template_test.cpp中定義方法體。

一些編譯器可以處理這個分割,但是你必須記住,在一個級別上,templates are like macros。爲了讓編譯器爲您的特定模板生成模板,它需要使模板源代碼方便。

-1

當編譯器編譯main.cpp時,它看到類定義具有成員函數聲明,但沒有成員函數定義。它只是假定在某處必須有「模板化」構造函數和Foo實現的定義,因此它要求鏈接器在鏈接時找到它。

解決您的問題的方法是將模板的實現放入template.h中。

template<class BaseClass> 
class Templated : public BaseClass 
    { 
public: 
    Templated(int a) : BaseClass(a) {} 
    virtual int Foo() { return BaseClass::b; } 
    }; 

有趣的是,我能得到你的代碼將這個標籤在template_test.cpp結束鏈接。

void Nobody_Ever_Calls_This() 
{ 
    Templated<Base> dummy(1); 
} 

現在編譯器可以找到一個模板鏈接的實例。我不會推薦這個技術。某些其他文件可能需要創建一個

Templated<Widget> 

然後您必須添加另一個顯式實例化到template_test.cpp。

2

問題是,當您的模板文件編譯時,編譯器不知道它將需要生成代碼的類型,所以它不會。

然後,當您鏈接時,main.cpp說它需要這些函數,但它們從未編譯到目標文件中,因此鏈接程序找不到它們。

其他答案顯示了以便攜方式解決此問題的方法,實質上是將模板化成員函數的定義放置在從實例化該類實例的位置可見的位置 - 無論是通過顯式實例化還是將實現放在一個從main.cpp包含的文件中。

您可能還想閱讀您的編譯器文檔,看看他們如何建議設置。我知道IBM XLC編譯器有一些不同的設置和選項來設置它們。