【问题标题】:Inheriting from a virtual template class in C++从 C++ 中的虚拟模板类继承
【发布时间】:2009-12-14 19:49:47
【问题描述】:

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

// 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&lt;std::string&gt;::Foo()Derived&lt;std::string&gt;::Bar()Derived&lt;int&gt; 也是如此。

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

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

【问题讨论】:

  • 一个好的做法是将模板的实现放在同一个标​​题中。 AFAIK :)
  • 在许多编译器中,这不仅是一种好的做法,而且是必需的。

标签: c++ inheritance templates


【解决方案1】:

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

我认为有些编译器在编译和链接步骤之间有联系,这些步骤注意到对缺少模板实例的引用,然后从声明它们的 .cpp 文件中实例化它们。但我不知道它们是什么,而且功能非常罕见。

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      即使使用 Comeau,您也必须使用 export 关键字才能正常工作。由于他们是唯一实现 export 的人,因此很有可能您并不在意。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-04
        • 2011-03-20
        • 1970-01-01
        • 1970-01-01
        • 2020-03-10
        • 2012-02-07
        相关资源
        最近更新 更多