“Referencia indefinida para” constructor de clase de plantilla

No tengo idea de por qué sucede esto, ya que creo que tengo todo correctamente declarado y definido.

Tengo el siguiente progtwig, diseñado con plantillas. Es una implementación simple de una cola, con las funciones de miembro “agregar”, “restar” e “imprimir”.

He definido el nodo para la cola en el fino “nodo_colaystack.h”:

#ifndef NODO_COLAYPILA_H #define NODO_COLAYPILA_H #include  template  class cola; template  class nodo_colaystack { T elem; nodo_colaystack* sig; friend class cola; public: nodo_colaystack(T, nodo_colaystack*); }; 

Luego la implementación en “nodo_colaystack.cpp”

 #include "nodo_colaystack.h" #include  template  nodo_colaystack::nodo_colaystack(T a, nodo_colaystack* siguiente = NULL) { elem = a; sig = siguiente;//ctor } 

Después, la definición y statement de la clase de plantilla de cola y sus funciones:

“cola.h”:

 #ifndef COLA_H #define COLA_H #include "nodo_colaystack.h" template  class cola { nodo_colaystack* ult, pri; public: cola(); void anade(T&); T saca(); void print() const; virtual ~cola(); }; #endif // COLA_H 

“cola.cpp”:

 #include "cola.h" #include "nodo_colaystack.h" #include  using namespace std; template  cola::cola() { pri = NULL; ult = NULL;//ctor } template  void cola::anade(T& valor) { nodo_colaystack  * nuevo; if (ult) { nuevo = new nodo_colaystack (valor); ult->sig = nuevo; ult = nuevo; } if (!pri) { pri = nuevo; } } template  T cola::saca() { nodo_colaystack  * aux; T valor; aux = pri; if (!aux) { return 0; } pri = aux->sig; valor = aux->elem; delete aux; if(!pri) { ult = NULL; } return valor; } template  cola::~cola() { while(pri) { saca(); }//dtor } template  void cola::print() const { nodo_colaystack  * aux; aux = pri; while(aux) { cout <elem <sig; } } 

Entonces, tengo un progtwig para probar estas funciones de la siguiente manera:

“main.cpp”

 #include  #include "cola.h" #include "nodo_colaystack.h" using namespace std; int main() { float a, b, c; string d, e, f; cola flo; cola str; a = 3.14; b = 2.71; c = 6.02; flo.anade(a); flo.anade(b); flo.anade(c); flo.print(); cout << endl; d = "John"; e = "Mark"; f = "Matthew"; str.anade(d); str.anade(e); str.anade(f); cout << endl; c = flo.saca(); cout << "First In First Out Float: " << c << endl; cout << endl; f = str.saca(); cout << "First In First Out String: " << f << endl; cout << endl; flo.print(); cout << endl; str.print(); cout << "Hello world!" << endl; return 0; } 

Pero cuando construyo, el comstackdor arroja errores en cada instancia de la clase de plantilla:

referencia indefinida a `cola (float) :: cola () ‘… (en realidad es cola’ ‘:: cola (), pero esto no me permite usarlo así).

Y así. En total, 17 advertencias, contando las de las funciones miembro que se llaman en el progtwig.

¿Por qué es esto? Esas funciones y constructores se definieron. Pensé que el comstackdor podría reemplazar la “T” en la plantilla con “float”, “string” o lo que sea; esa era la ventaja de usar plantillas.

Leí en algún lado que debería poner la statement de cada función en el archivo de encabezado por algún motivo. ¿Está bien? Y si es así, ¿por qué?

Gracias por adelantado.

Esta es una pregunta común en la progtwigción C ++. Hay dos respuestas válidas para esto. Hay ventajas y desventajas en ambas respuestas y su elección dependerá del contexto. La respuesta común es poner toda la implementación en el archivo de encabezado, pero hay otro enfoque que será adecuado en algunos casos. La decisión es tuya.

El código en una plantilla es simplemente un ‘patrón’ conocido por el comstackdor. El comstackdor no comstackrá los constructores cola::cola(...) y cola::cola(...) hasta que sea forzado a hacerlo. Y debemos asegurarnos de que esta comstackción ocurra para los constructores al menos una vez en todo el proceso de comstackción, o obtendremos el error de “referencia no definida”. (Esto se aplica a los otros métodos de cola también).

Comprender el problema

El problema es causado por el hecho de que main.cpp y cola.cpp se comstackrán por separado primero. En main.cpp , el comstackdor instanciará implícitamente las clases de plantilla cola y cola porque esas instanciaciones particulares se usan en main.cpp . La mala noticia es que las implementaciones de esas funciones miembro no están en main.cpp , ni en ningún archivo de encabezado incluido en main.cpp , y por lo tanto el comstackdor no puede incluir versiones completas de esas funciones en main.o Al comstackr cola.cpp , el comstackdor tampoco comstackrá esas instancias, porque no hay instancias implícitas o explícitas de cola o cola . Recuerde, al comstackr cola.cpp , el comstackdor no tiene idea de qué instancias se necesitarán; ¡y no podemos esperar que compile para cada tipo para garantizar que este problema nunca suceda! ( cola , cola , cola , cola< cola > … y así sucesivamente …)

Las dos respuestas son:

  • Indique al comstackdor, al final de cola.cpp , qué clases de plantilla concretas serán necesarias, obligándolo a comstackr cola y cola .
  • Coloque la implementación de las funciones miembro en un archivo de encabezado que se incluirá cada vez que cualquier otra ‘unidad de traducción’ (como main.cpp ) use la clase de plantilla.

Respuesta 1: crea una instancia explícita de la plantilla y sus definiciones de miembro

Al final de cola.cpp , debe agregar líneas que ejemplifiquen explícitamente todas las plantillas relevantes, como

 template class cola; template class cola; 

y agrega las dos líneas siguientes al final de nodo_colaystack.cpp :

 template class nodo_colaystack; template class nodo_colaystack; 

Esto asegurará que, cuando el comstackdor comstack cola.cpp , compile explícitamente todo el código de las clases cola y cola . Del mismo modo, nodo_colaystack.cpp contiene las implementaciones de las nodo_colaystack<...> .

En este enfoque, debe asegurarse de que toda la implementación se coloque en un archivo .cpp (es decir, una unidad de traducción) y que la .cpp explícita se coloque después de la definición de todas las funciones (es decir, al final del archivo).

Respuesta 2: copie el código en el archivo de encabezado relevante

La respuesta común es mover todo el código de los archivos de implementación cola.cpp y nodo_colaystack.cpp a cola.h y nodo_colaystack.h . A la larga, esto es más flexible, ya que significa que puede usar instancias adicionales (por ejemplo, cola ) sin más trabajo. Pero podría significar que las mismas funciones se comstackn muchas veces, una vez en cada unidad de traducción. Este no es un gran problema, ya que el enlazador ignorará correctamente las implementaciones duplicadas. Pero podría ralentizar la comstackción un poco.

Resumen

La respuesta predeterminada, utilizada por el STL por ejemplo y en la mayoría del código que cualquiera de nosotros escribirá, es poner todas las implementaciones en los archivos de encabezado. Pero en un proyecto más privado, tendrá más conocimiento y control de las clases de plantillas particulares que se crearán instancias. De hecho, este ‘error’ podría ser visto como una característica, ya que impide que los usuarios de su código accidentalmente utilicen instancias que no haya probado o planificado (“Sé que esto funciona para cola y cola , si desea utilizar algo más, dígame primero y podrá verificar que funcione antes de habilitarlo. “).

Finalmente, hay otros tres errores menores en el código en su pregunta:

  • Te falta un #endif al final de nodo_colaystack.h
  • en cola.h nodo_colaystack* ult, pri; debería ser nodo_colaystack *ult, *pri; – Ambos son punteros.
  • nodo_colaystack.cpp: El parámetro predeterminado debe estar en el archivo de cabecera nodo_colaystack.h , no en este archivo de implementación.

Deberá definir las funciones dentro de su archivo de encabezado.
No puede separar la definición de funciones de plantilla en el archivo de origen y las declaraciones en el archivo de encabezado.

Cuando se utiliza una plantilla de una manera que desencadena su intstantation, un comstackdor necesita ver esa definición de plantillas en particular. Este es el motivo por el que las plantillas a menudo se definen en el archivo de encabezado en el que se declaran.

Referencia:
Estándar C ++ 03, § 14.7.2.4:

La definición de una plantilla de función no exportada, una plantilla de función miembro no exportada, o una función miembro no exportada o un miembro de datos estáticos de una plantilla de clase debe estar presente en cada unidad de traducción en la que está explícitamente instanciada.

EDITAR:
Para aclarar la discusión sobre los comentarios:
Técnicamente, hay tres formas de evitar este problema de vinculación:

  • Para mover la definición al archivo .h
  • Agregue instancias explícitas en el archivo .cpp .
  • .cpp archivo .cpp que define la plantilla en el archivo .cpp utilizando la plantilla.

Cada uno de ellos tiene sus pros y sus contras,

Mover las definiciones a los archivos de encabezado puede boost el tamaño del código (los comstackdores de hoy en día pueden evitarlo) pero seguramente boostá el tiempo de comstackción.

El uso del enfoque de creación de instancias explícitas está volviendo al enfoque tradicional similar a macro. Otra desventaja es que es necesario saber qué tipos de plantilla necesita el progtwig. Para un progtwig simple, esto es fácil, pero para el progtwig complicado esto se vuelve difícil de determinar de antemano.

Si bien incluir archivos cpp es confuso al mismo tiempo, comparte los problemas de ambos enfoques anteriores.

Encuentro que el primer método es el más fácil de seguir y de implementar, y por lo tanto me gusta usarlo.

Este enlace explica dónde te estás equivocando:

[35.12] ¿Por qué no puedo separar la definición de mi clase de plantillas de su statement y ponerla dentro de un archivo .cpp?

Coloque la definición de sus constructores, métodos de destructores y otras cosas en su archivo de encabezado, y eso corregirá el problema.

Esto ofrece otra solución:

¿Cómo puedo evitar errores del enlazador con mis funciones de plantilla?

Sin embargo, esto requiere que anticipe cómo se utilizará su plantilla y, como solución general, no es intuitiva. Sin embargo, resuelve el caso de esquina donde se desarrolla una plantilla para ser utilizada por algún mecanismo interno, y se quiere controlar la manera en que se usa.

    Intereting Posts