不会生成模板中的虚拟方法
Virtual methods in templates are not generated
可能的重复:
为什么使用模板时出现"未解析的外部符号"错误?
我有一套有点复杂的课程。
具有子参数化 A 类的类 _A,该类有两个子级 A1 和 A2。
类 B. 包含作为成员的类_A对象的指针。有两个子类 B1 和 B2,分别对应于类 A1 和 A2。B1 将_A构造为 A1。B2 作为 A2 尊重。
最后是类 Y,它在类 B 中定义了子 BY.
现在它是如何在文件中存在的。
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
简而言之,您不能将模板定义放入 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
,当编译器看到您正在使用类型int
实例化templateObj
时,它会去寻找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"
==> 哪个#include <iostream>
...
tfmain.cpp
#include "tf2.h"
==> 哪个#include "tf1.h"
==>哪个#include <iostream>
...
tf1.cpp
有什么?它知道什么?
- 知道
Y
和_A
的声明 - 知道
A
、A1
和A2
的模板声明 - 具有
A
、A1
和A2
中各种方法的模板定义
tfmain.cpp
有什么?它知道什么?
- 知道
B
、B1
、B2
、Y
和_A
的声明 - 知道
A
、A1
和A2
的模板声明 - 在
B
、B1
、B2
中对各种方法有定义 - 具有
main
和B2
的实例
所以,现在的问题是:
tf1.cpp
知道tf2.h
或tfmain.cpp
吗?它是否知道您正在创建一个实例来B2
该实例使用类型BY
实例化A2
?
tfmain.cpp
知道tf1.cpp
吗?它是否知道A
、A1
或A2
中方法的定义?
它们彼此不认识,因此编译器无法在翻译单元tf1.o
中为模板类生成定义代码(此时,它甚至不知道您正在创建一个实例到 B2,该实例实例化A2
类型为BY
)。
最后,链接器无法找到tfmain.o
请求的代码,因为它不存在。
不能将模板化函数定义放在.cpp
文件中,必须将它们放在头文件中。这是模板的一个奇怪的限制。
这与以下事实有关:函数的模板化版本仅在使用时生成。因此,您无法预编译模板化函数,因为它尚未使用,因此它不会生成所有内容。
Xymostech回答了你的问题,但我不同意这个限制很奇怪。 ;) 这应该为您澄清此事。
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?
- 如何编写 operator= 用于使用虚拟方法与非平凡成员的匿名联合
- 让编译器告诉什么确切的纯虚拟方法使结构抽象?
- 使用模板而不是虚拟方法的管道模式
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 为什么调用没有正文的纯虚拟方法不会导致链接器错误?
- 出于什么目的,非虚拟方法将与C++一起使用?
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 如何重写继承的嵌套类中存在的虚拟方法
- 私有虚拟方法有什么用?
- 基类可以声明虚拟方法但不定义它吗?仍然在派生类中定义
- googletest:测试基类具有纯虚拟方法的派生类时的核心转储
- 确保模拟的 GTest 方法覆盖虚拟方法
- CPP 继承虚拟方法解析顺序
- 我是否应该在包含虚拟方法的类上使用'memcpy'?如果没有,如何替换它?
- 用c++中的纯虚拟方法抽象模板类
- 解决虚拟方法的歧义继承的两种方法
- 没有针对完全专用模板类的外联虚拟方法定义