2008-10-16 79 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编译器有一些不同的设置和选项来设置它们。