可能重复:
Why do I get “unresolved external symbol” errors when using templates?
我有一套复杂的课程 具有子参数化类A的类_A,其具有两个子节点A1和A2 类B.包含类_A的对象上的指针作为成员。有两个子类B1和B2,分别对应A1和A2类。 B1构造_A作为A1。 B2分别为A2 最后在类B中定义了子BY的类Y 现在它是如何出现在文件中的。
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");
}
最后,这个问题。
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()
我正在使用MinGW v4.7.2
答案 0 :(得分:3)
您不能将模板化函数定义放在.cpp
文件中,您必须将它们放在头文件中。这是模板的奇怪限制。
它与您的函数的模板化版本仅在使用时生成的事实有关。因此,您无法预编译模板化函数,因为它尚未使用,因此它不会生成所有内容。
答案 1 :(得分:3)
简单来说,你不能把模板定义放到cpp文件中(有变通方法......但不是很愉快)
原因是编译器只在您调用该模板的第一个点实例化一个模板,因为编译器需要知道用什么类型来实例化模板。
现在,如果您将模板定义放入单独的cpp文件中,该文件将单独编译到其自己的翻译单元中。编译器在哪里查找实例化模板的情况?
例如
// Template.h
template <typename T>
class templateObj { ~templateObj(); };
// Template.cpp
#include "Template.h"
template <typename T>
templateObj::~templateObj() { /* delete stuff! */ }
// YourFile.cpp
#include "Template.h"
templateObj<int> myObj;
现在,当编译器编译此代码时。
它将生成两个翻译单元,一个用于Template.cpp
,另一个用于YourFile.cpp
。
请注意,Template.cpp
没有一个关于YourFile.cpp
是什么以及内部含义的线索。因此,无法在YourFile.cpp
中了解您使用templateObj
模板参数类型int
。因此,在Template.cpp
的结果转换单元中,编译器将不生成templateObj
的析构函数的实例化版本
现在,让我们看看YourFile.cpp
,当编译器发现您正在使用类型templateObj
实例化int
时,它会查找templateObj
的定义。由于您已包含Template.h
,因此编译器会发现您已为templateObj
声明了析构函数。但定义在哪里?
编译器没有看到~templateObj()
的定义,也不知道此时要查找的位置。
因此它只是暂缓,并将传递给链接器以搜索要链接的正确模块。
现在问题在于:
在编译器刚刚生成的两个翻译单元中,YourFile.o
或Template.o
都没有template<int>::~template()
的定义。
链接器读入YourFile.o
并期望将templateObj
的析构函数版本链接到,但唯一的其他翻译单元Template.o
没有它;事实上,它有没有。
那么现在呢?链接器必须抱怨...这就是你得到的错误信息。
关于代码发生的更多细节:
tf1.o
和tfmain.o
tf1.o
是根据tf1.cpp
及其包含的内容生成的。tfmain.o
是根据tfmain.cpp
生成的,无论其包含的内容。那么他们包括什么? tf1.cpp
和tfmain.cpp
了解其余代码的内容是什么?
tf1.cpp
#include "tf1.h"
==&gt; #include <iostream>
... tfmain.cpp
#include "tf2.h"
==&gt;其中#include "tf1.h"
==&gt; #include <iostream>
... tf1.cpp
有什么作用?它知道什么?
Y
和_A
A
,A1
和A2
A
,A1
和A2
tfmain.cpp
有什么作用?它知道什么?
B
,B1
,B2
,Y
和_A
<的 声明 / LI>
A
,A1
和A2
B
,B1
,B2
main
和B2
所以,现在的问题是:
tf1.cpp
对tf2.h
或tfmain.cpp
有何了解?是否知道您正在为B2
创建一个实例A2
,其类型为BY
?tfmain.cpp
是否了解tf1.cpp
的任何内容?它是否知道A
,A1
或A2
?? 他们 NOT 互相认识,因此编译器无法为翻译单元tf1.o
中的模板类生成定义代码(此时,它甚至不知道你正在为B2创建一个实例A2
类型BY
)的实例。
最后,链接器无法找到tfmain.o
要求的代码,因为它是 NOT 。
答案 2 :(得分:0)
Xymostech回答了你的问题,但我不同意这种限制很奇怪。 ;)This should clarify this matter for you.