2

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

我有一些复杂的类。
类 _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

4

3 回答 3

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使用 type进行实例化时int,它会去寻找 的定义templateObj。既然你已经包含Template.h了,编译器就会看到你已经声明了一个析构函数templateObj。但是定义在哪里?

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

现在问题来了:

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

所以现在怎么办?链接器必须抱怨......这就是您收到的错误消息。


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

  • 产生了两个翻译单元:tf1.otfmain.o
  • tf1.otf1.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
  • main一个实例B2

所以,现在的问题是:

对or有什么tf1.cpp了解吗?它是否知道您正在创建一个使用 type实例化的实例?tf2.htfmain.cppB2A2BY

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

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

最后,链接器无法找到所需的代码tfmain.o,因为它存在。

于 2012-11-28T20:00:18.600 回答
3

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

这与函数的模板版本仅在使用时生成这一事实有关。因此,您不能预编译模板化函数,因为它还没有被使用过,所以它不会生成所有内容。

于 2012-11-28T19:47:52.183 回答
0

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

于 2012-11-28T19:51:36.217 回答