Primero debes entender el mecanismo de las plantillas.Las plantillas no se compilan, se crean instancias cuando se usan y luego se compila su instanciación. Por lo tanto, el compilador necesita tener la definición de plantilla completa en cada módulo utilizando la función de plantilla, para crear una instancia primero de acuerdo con los parámetros que haya pasado.
Para resolver su problema, hay tres soluciones, pero verá que ambas conducen al mismo resultado. O te enteras aplicar sus plantillas en el archivo de cabecera dentro de la definición de clase (que utilizar al sufijo con .hxx en lugar de .h con el fin de precisos que están las definiciones que contiene plantillas):
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T& t) {
t.doSomething();
}
};
#endif
o puede hacer externalizar la definición de la clase, pero todavía en el archivo de cabecera:
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T&);
};
template <class T>
void Foo::bar(const T& t) {
t.doSomething();
}
#endif
por último, se puede implementar métodos cuerpos plantilla en un archivo externo (con el prefijo .cxx por la misma razón). Contendrá cuerpos de métodos pero no incluirá "Foo.hxx". En cambio, es "Foo.hxx" que incluirá "Foo.cxx" después de la definición de la clase. De esta manera, cuando el compilador resuelve la directiva #include, encuentra toda la definición de plantilla en el mismo módulo, lo que le permite crear una instancia que:
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T&);
};
#include "Foo.cxx"
#endif
// Foo.cxx
template <class T>
void Foo::bar(const T& t) {
t.doSomething();
}
La elección entre estas 3 formas de implementar plantillas es más bien una cuestión de legibilidad (y gusto).
Segundo y tercero son equivalentes en términos de código generado, pero prefiero no usar la solución de archivo cxx, porque a menudo conduce a errores estúpidos cuando se olvida de invertir el include.
Además, las conocidas librerías C++ como STL o Boost proponen su código solo en archivos de encabezado, lo que es un signo de buen diseño. Al usar la definición externa dentro de los encabezados, aclaras la definición de tu clase. También evita que el compilador ingrese automáticamente los métodos, lo que a veces puede generar resultados deficientes según Herb Sutter http://www.gotw.ca/gotw/033.htm
En el caso en que se necesita una plantilla en un solo archivo CPP, entonces pondría la declaración y la definición solo en ese archivo. Si es una plantilla de función miembro y el resto de la clase se usa en otras unidades de traducción, definitivamente pondré la definición en el archivo .h. Incluso me preocuparía el comportamiento indefinido o definido por la implementación si esto no se hiciera. –
@Marcus, debería haber mencionado que en la pregunta original - sí Foo :: doSomething() es una función de miembro de plantilla privada de la clase Foo. –
@John, no sigo tu punto, ¿podrías elaborar un poco? En mi caso, tengo una función de miembro de plantilla privada de la clase Foo, por lo tanto, solo la usa la implementación de la clase Foo en Foo.cpp. Entonces eso parece encajar con el primer caso/oración en tu comentario. Pero mi situación también se ajusta a la descripción en la segunda oración de tu comentario ("plantilla de función de miembro y el resto de la clase se usa en otras unidades de traducción") donde dices que la pongas en un archivo .h. –