不会生成模板中的虚拟方法

Virtual methods in templates are not generated

本文关键字:虚拟 方法      更新时间:2023-10-16

可能的重复:
为什么使用模板时出现"未解析的外部符号"错误?

我有一套有点复杂的课程。
具有子参数化 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.cppYourFile.cpp是什么以及里面有什么一无所知。所以它无法知道在YourFile.cpp中,你已经将templateObj与模板参数类型一起使用int。因此,在生成的翻译单元中Template.cpp,编译器不会生成templateObj的析构函数的实例化版本

现在,让我们看一下YourFile.cpp,当编译器看到您正在使用类型int实例化templateObj时,它会去寻找templateObj的定义。由于您已经包含了Template.h,编译器会看到您已经为templateObj声明了一个析构函数。但是定义在哪里??

编译器看不到~templateObj()的定义,也不知道此时在哪里寻找。 因此,它只是推迟并将传递给链接器以搜索要链接到的正确模块。

现在问题来了:

在编译器刚刚生成的两个翻译单元中,YourFile.oTemplate.o都没有template<int>::~template()的定义。 链接器以YourFile.o读取,并期望具有要链接到的templateObj析构函数版本,但唯一的其他翻译单元Template.o没有它;事实上,它什么都没有

那现在怎么办?链接器不得不抱怨...这就是您收到的错误消息。


有关代码发生的情况的更多详细信息:

  • 制作两个翻译单元:tf1.otfmain.o
  • tf1.o是由tf1.cpp和它所包含的任何内容生成的。
  • tfmain.o是由tfmain.cpp和它所包含的任何内容生成的。

那么它们包括什么呢?tf1.cpptfmain.cpp对代码的其余部分了解多少?

tf1.cpp

  • #include "tf1.h"==> 哪个#include <iostream>...

tfmain.cpp

  • #include "tf2.h"==> 哪个#include "tf1.h"==>哪个#include <iostream>...

tf1.cpp有什么?它知道什么?

  • 知道Y_A的声明
  • 知道AA1A2的模板声明
  • 具有AA1A2中各种方法的模板定义

tfmain.cpp有什么?它知道什么?

  • 知道BB1B2Y_A声明
  • 知道AA1A2的模板声明
  • BB1B2中对各种方法有定义
  • 具有mainB2的实例

所以,现在的问题是:

tf1.cpp知道tf2.htfmain.cpp吗?它是否知道您正在创建一个实例来B2该实例使用类型BY实例化A2

tfmain.cpp知道tf1.cpp吗?它是否知道AA1A2中方法的定义

它们彼此认识,因此编译器无法在翻译单元tf1.o中为模板类生成定义代码(此时,它甚至不知道您正在创建一个实例到 B2,该实例实例化A2类型为BY)。

最后,链接器无法找到tfmain.o请求的代码,因为它不存在

不能将模板化函数定义放在.cpp文件中,必须将它们放在头文件中。这是模板的一个奇怪的限制。

这与以下事实有关:函数的模板化版本仅在使用时生成。因此,您无法预编译模板化函数,因为它尚未使用,因此它不会生成所有内容。

Xymostech回答了你的问题,但我不同意这个限制很奇怪。 ;) 这应该为您澄清此事。