2

Consider following code:

myclass.h :

template <class T>
struct S {
    void f();
};

struct MyClass : public S<int>
{
    void g();
};

myclass.cpp :

#include "myclass.h"
#include <iostream>

template <class T>
void S<T>::f()
{
    std::cout << "f()\n";
    /* some code here that could not be in header */
}

void MyClass::g()
{
    std::cout << "g()\n";
}

main.cpp :

#include "myclass.h"

int main()
{
    MyClass m;
    m.g();
    m.f(); // problem here
}

I've got linker error:

undefined reference to `S::f()'

Can I solve this issue without transferring implementation of S::f() to header file? Why S::f() not instantiated when I declare a MyClass derived from full specialized template base class?

αλεχολυτ
  • 4,792
  • 1
  • 35
  • 71
  • 1
    possible duplicate of [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – molbdnilo Jan 29 '15 at 10:43
  • The code that should not be in the header, can it be put in a static or global (non-templated) function? Or using e.g. the [pimpl idiom](http://en.wikipedia.org/wiki/Opaque_pointer)? – Some programmer dude Jan 29 '15 at 10:44

3 Answers3

1

Why S::f() not instantiated when I declare a MyClass derived from full specialized template base class?

Because your myclass.cpp makes no use of that template, and like all unused templates in translation units, they amount to nothing in the produced code. The derivation is all well and good, and if MyClass::g() used S<T>::f() you would pull in that code and life would be good. But you don't, so you have to pull it in another way...

You should be able to do this with explicit instantiation. Note the following addition to your .cpp file:

#include "myclass.h"
#include <iostream>

template <class T>
void S<T>::f()
{
    std::cout << "f()\n";
}

void MyClass::g()
{
    std::cout << "g()\n";
}

template struct S<int>; // ADDED

Be warned, however. This will only work if the only usage is S<int>. Additional expansions would need additional entries of explicit instantiations.

You could also do it with direct specialization, such as changing your .cpp to just do this:

#include "MyClass.h"
#include <iostream>

template <>
void S<int>::f()
{
    std::cout << "f()\n";
}

void MyClass::g()
{
    std::cout << "g()\n";
}

but that would be rather limiting in my mind, as each additional type you add would need its own specialization.

Anyway, best of luck.

WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • Thank you very much. I tried an explicit instantiation, but did so above a implementation of f(). When I move it down - code compiled successfully. – αλεχολυτ Jan 29 '15 at 11:29
1

Adding an explicit instantiation

template
struct S<int>;

to "myclass.cpp" makes the code compile.

Loosely speaking, the derivation from S<int> only "instantiates" the class definition, not the members.
There is no way for the compiler to instantiate f at that point since the definition of f isn't known.

You'll need to provide explicit instantiations for all the types you're using, which somewhat limits the utility of the template.

A popular way of keeping template definitions out of headers is to have them in their own file which is #included at the end of the header.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

You can't solve this problem without transferring implementation of s::f() function to header file. this is because compiler must know implementation of template before using it.