Possible Duplicate:
Why do I get “unresolved external symbol” errors when using templates?
I have a little bit complicated set of classes.
Class _A which has child parametrized class A which has two children A1 and A2.
Class B. Contains pointer on object of class _A as member. Has two child classes B1 and B2 which correspond to classes A1 and A2 each. B1 constructs _A as A1. B2 as A2 respectivelly.
And finally class Y which has child BY defined inside of class B.
Now how it is present in files.
tf1.h
#include <iostream>
struct Y{ // goes to the file tf2.h as ancestor of the class B::BY
};
struct _A{ // goes to the file tf2.h as a member of the class B
};
template<class X>
struct A: public _A { // goes to two classes below only as an ancestor
virtual void method();
protected:
virtual void m() = 0;
};
template<class X>
struct A1: public A<X>{ // goes to the file tf2.h to the class B1
protected:
void m();
};
template<class X>
struct A2: public A<X>{ // goes to the file tf2.h to the class B2
protected:
void m();
};
tf1.cpp
#include "tf1.h"
template<class X>
void A<X>::method(){
/* here the class X used */
std::cout << "A::method called" << std::endl;
m();
}
template<class X>
void A1<X>::m(){
std::cout << "A1::m called" << std::endl;
}
template<class X>
void A2<X>::m(){
std::cout << "A1::m called" << std::endl;
}
tf2.h
#include "tf1.h"
class B{ // is the counterpain of the class _A
protected:
class BY: public Y{
};
_A * mp_x;
};
class B1: public B{ // is the counterpain of the class A1
public:
B1(){mp_x = new A1<BY>; std::cout << "B::B is called" << std::endl;}
};
class B2: public B{ // is the counterpain of the class A2
public:
B2(){mp_x = new A2<BY>; std::cout << "C::C is called" << std::endl;}
};
tfmain.cpp
#include <stdlib.h>
#include "tf2.h"
int main (int,char**){
B2 b2;
system ("PAUSE");
}
And, finally, the problem.
d:>g++ -std=c++0x tfmain.cpp -o tfmain.exe
ccB2BnSP.o:tfmain.cpp:(.rdata$_ZTV2A2IN1B2BYEE[__ZTV2A2IN1B2BYEE]+0x8): undefined reference to `A<B::BY>::method()'
ccB2BnSP.o:tfmain.cpp:(.rdata$_ZTV2A2IN1B2BYEE[__ZTV2A2IN1B2BYEE]+0xc): undefined reference to `A2<B::BY>::m()'
ccB2BnSP.o:tfmain.cpp:(.rdata$_ZTV1AIN1B2BYEE[__ZTV1AIN1B2BYEE]+0x8): undefined reference to `A<B::BY>::method()
I am using MinGW v4.7.2
To put it simply, you can’t put template definitions into a cpp file (there are workarounds… but not pleasant ones)
The reason is that the compiler only instantiates a template at the first point you invoke that template, since the compiler needs to know what type to instantiates the template with.
Now, if you put the template definition into a separate cpp file which gets compiled separately into its own translation unit. Where does the compiler look for cases where you instantiate the template?
For example
Now, when the compiler compiles this code.
It will generate two translation units, one for
Template.cpp, the other forYourFile.cpp.Note that
Template.cpphas not a single clue of whatYourFile.cppis and what’s inside it. So it has no way of knowning that inYourFile.cpp, you have usedtemplateObjwith a template parameter typeint. Because of this, in the resulting translation unit ofTemplate.cpp, the compiler will not generate an instantiated version of the destructor oftemplateObjNow, let’s look at
YourFile.cpp, when the compiler sees that you’re instantiatingtemplateObjwith typeint, it goes to look for the definition oftemplateObj. Since you have includedTemplate.h, the compiler sees that you have declared a destructor fortemplateObj. But where is the definiton??The compiler doesnt see the definition of
~templateObj(), nor does it know where to look for at this point.So it simply holds off and will pass on to the linker to search for the correct module to link to.
Now here is the problem:
In the two translation units the compiler has just produced, neither
YourFile.oorTemplate.ohas the definition fortemplate<int>::~template().The linker reads in
YourFile.oand expects to have that version of the destructor oftemplateObjto link to, but the only other translation unitTemplate.odoesn’t have it; in fact, it has nothing.So what now? The linker has to complain… and that’s the error message you get.
A bit more details about what happened with your code:
tf1.oandtfmain.otf1.ois generated fromtf1.cppand whatever it includes.tfmain.ois generated fromtfmain.cppand whatever itcludes.So what do they include? What does
tf1.cppandtfmain.cppknow about the rest of the code?tf1.cpp#include "tf1.h"==> which#include <iostream>…tfmain.cpp#include "tf2.h"==> which#include "tf1.h"==> which#include <iostream>…What does
tf1.cpphave? What does it know?Yand_AA,A1andA2A,A1, andA2What does
tfmain.cpphave? What does it know?B,B1,B2,Yand_AA,A1andA2B,B1,B2mainand an instance ofB2So, now the questions are:
Does
tf1.cppknow anything abouttf2.hortfmain.cpp? Does it know that you’re creating an instance toB2which instantiatesA2with typeBY?Does
tfmain.cppknow anything abouttf1.cpp? Does it know the definitions of methods inA,A1orA2??They do NOT know each other, and thus the compiler has no way to generate the code for the definiton to your template classes in the translation unit
tf1.o(At that point, it doesn’t even know you’re creating an instance to B2 which instantiatesA2with typeBY).And finally the linker has no way to find the code
tfmain.ois asking for, since it is NOT there.