Возможный дубликат:
Почему я получаю ошибки «неразрешенный внешний символ» при использовании шаблонов?
У меня есть немного сложный набор классов.
Класс _A, у которого есть дочерний параметризованный класс A, у которого есть два дочерних элемента A1 и A2.
Класс B. Содержит указатель на объект класса _A в качестве члена. Имеет два дочерних класса B1 и B2, которые соответствуют классам A1 и A2 каждый. B1 создает _A как A1. B2 как A2 соответственно.
И, наконец, класс Y, у которого есть дочерний BY, определенный внутри класса B.
Теперь, как это присутствует в файлах.
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
, когда компилятор видит, что вы создаете экземпляр 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"
==> который #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
который создает A2
с типом BY
?tfmain.cpp
знать что-нибудь о tf1.cpp
? Знает ли это определения методов в A
, A1
или же A2
??Они делают НЕ знать друг друга, и, таким образом, компилятор не может сгенерировать код для определения ваших шаблонных классов в модуле перевода tf1.o
(На тот момент, он даже не знает, что вы создаете экземпляр для B2, который создает A2
с типом BY
).
И, наконец, компоновщик не может найти код tfmain.o
просит, так как это НЕ там.
Вы не можете поместить свои определения шаблонных функций в .cpp
файлы, вы должны поместить их в заголовочные файлы. Это странное ограничение шаблонов.
Это связано с тем, что шаблонные версии ваших функций генерируются только тогда, когда они используются. Таким образом, вы не можете предварительно скомпилировать шаблонную функцию, потому что она еще не использовалась, поэтому она не генерирует все.
Xymostech ответил на ваш вопрос, но я не согласен, что это ограничение странно. 😉 Это должно прояснить этот вопрос для вас.