从C ++中的虚拟模板类继承

时间:2009-12-14 19:49:47

标签: c++ inheritance templates

如何从虚拟模板类继承,在此代码中:

// 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或DInt时,链接器会抱怨有未解析的外部因素,Derived<std::string>::Foo()Derived<std::string>::Bar()Derived<int>也是如此。

我的代码中是否遗漏了某些内容?

编辑: 谢谢大家。现在很清楚了。

3 个答案:

答案 0 :(得分:6)

您必须确保成员函数在某处实例化所有必需类型。

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

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

答案 1 :(得分:6)

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

我认为有些编译器在编译和链接步骤之间有联系,它们会注意对缺少的模板实例的引用,并从声明它们的.cpp文件中实例化它们。但我不知道它们是哪一个,而且功能极为罕见。

如果在头文件中定义它们,大多数编译器会将它们作为“弱”符号发送到它们被引用的每个编译单元中。除了弱符号的一个定义之外,链接器将抛弃所有内容。这会导致额外的编译时间。

或者,有一些语法用于显式实例化模板并强制编译器在那里发出定义。但这需要你了解T可能具有的所有价值,你将不可避免地错过一些。

答案 2 :(得分:3)

这与推导并没有多大关系。这只是模板的一般规则:对于大多数编译器(除了Comeau之外的任何东西),你必须将模板的完整实现放在每个将实例化该模板的翻译单元中 - 通常在标题中。

即使使用Comeau,您也必须使用export关键字才能使工作正常。由于他们是唯一实施export的人,所以你很可能不太在意。