views:

348

answers:

3

How do I inherit from a virtual template class, in this code:

// 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() { ... }

When I try to use the DStr or DInt, the linker complain that there are unresolved externals, which are Derived<std::string>::Foo() and Derived<std::string>::Bar(), and the same for Derived<int>.

Did I miss something in my code?

EDIT: Thanks all. It's pretty clear now.

+4  A: 

This doesn't really have much to do with derivation. It's just a general rule with templates: with most compilers (anything but Comeau) you have to put the full implementation of a template where it's visible in every translation unit that will instantiate that template -- normally in the header.

Even with Comeau, you have to use the export keyword to make things work right. Since They're the only ones that implement export, chances are pretty good that you don't care much though.

Jerry Coffin
+5  A: 

You have to ensure that the member functions are instantiate for all the required types somewhere.

Usually this is accomplished by defining template functions inline in the header file where they are declared so that any use of the functions will cause them to be instantiated.

As an alternative, you can use explicit instantiations in the source file where you define them but this does require you to know in advance all the types that your template will ever be instantiated for.

Charles Bailey
+4  A: 

You need to define template<typename T> std::string Derived<T>::Foo() { ... } and template<typename T> std::string Derived<T>::Bar() { ... } in the header file. When the compiler is compiling test.cpp it doesn't know all the possible values of T that you might use in other parts of the program.

I think there are some compilers that have connections between the compiling and linking steps that notice references to missing template instantiations and go instantiate them from the .cpp file where they are declared. But I don't know which they are, and the functionality is exceedingly rare to find.

If you define them in the header file most compilers will emit them as a 'weak' symbol into every compilation unit in which they're referenced. And the linker will toss out all except for one definition of the weak symbol. This causes extra compilation time.

Alternately, there are syntaxes for explicitly instantiating templates and forcing the compiler to emit definitions right there. But that requires you to be aware of all the values T could possibly have and you will inevitably miss some.

Omnifarious