2009-12-14 90 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

你必须确保成员函数实例化所有需要的类型。

通常这是通过在声明头文件中内联定义模板函数来完成的,因此任何对函数的使用都会导致它们被实例化。

作为替代方案,您可以在定义它们的源文件中使用显式实例,但这确实需要您事先知道您的模板将被实例化的所有类型。