Виртуальные методы в шаблонах не генерируются

Возможный дубликат:
Почему я получаю ошибки «неразрешенный внешний символ» при использовании шаблонов?

У меня есть немного сложный набор классов.
Класс _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

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 просит, так как это НЕ там.

3

Другие решения

Вы не можете поместить свои определения шаблонных функций в .cpp файлы, вы должны поместить их в заголовочные файлы. Это странное ограничение шаблонов.

Это связано с тем, что шаблонные версии ваших функций генерируются только тогда, когда они используются. Таким образом, вы не можете предварительно скомпилировать шаблонную функцию, потому что она еще не использовалась, поэтому она не генерирует все.

3

Xymostech ответил на ваш вопрос, но я не согласен, что это ограничение странно. 😉 Это должно прояснить этот вопрос для вас.

0
По вопросам рекламы [email protected]