Estoy recibiendo de error al intentar compilar C++ clase de plantilla, que se divide entre un .hpp y .cpp de archivo:

$ g++ -c -o main.o main.cpp  
$ g++ -c -o stack.o stack.cpp   
$ g++ -o main main.o stack.o  
main.o: In function `main':  
main.cpp:(.text+0xe): undefined reference to 'stack<int>::stack()'  
main.cpp:(.text+0x1c): undefined reference to 'stack<int>::~stack()'  
collect2: ld returned 1 exit status  
make: *** [program] Error 1  

Aquí está mi código:

de la pila.ch:

#ifndef _STACK_HPP
#define _STACK_HPP

template <typename Type>
class stack {
    public:
            stack();
            ~stack();
};
#endif

stack.cpp:

#include <iostream>
#include "stack.hpp"

template <typename Type> stack<Type>::stack() {
        std::cerr << "Hello, stack " << this << "!" << std::endl;
}

template <typename Type> stack<Type>::~stack() {
        std::cerr << "Goodbye, stack " << this << "." << std::endl;
}

main.cpp:

#include "stack.hpp"

int main() {
    stack<int> s;

    return 0;
}

ld es, por supuesto, correcto: los símbolos no están en stack.o.

La respuesta a esta pregunta no ayuda, como ya estoy haciendo lo que dice.

Este podría ayudar, pero no quiero mover cada método en la .hpp archivo—yo no tengo por qué debo?

Es la única solución razonable para mover todo en el .cpp archivo a la .hpp archivo, y simplemente lo incluyen todo, en lugar de enlace como un objeto independiente del archivo? Que parece muy feo! En ese caso, bien podría volver a mi estado anterior y cambiar el nombre de stack.cpp a stack.hpp y hacer con ella.

  • Hay dos grandes soluciones para cuando usted realmente quiere mantener su código oculto (en un archivo binario) o mantenerlo limpio. Es necesaria para reducir la generalidad a pesar de que en la primera situación. Se explica aquí: stackoverflow.com/questions/495021/…
InformationsquelleAutor exscape | 2009-11-12

15 Comentarios

  1. 140

    No es posible escribir la aplicación de una plantilla de clase por separado en un archivo cpp y compilar. Todas las maneras de hacerlo, si alguien afirma, son soluciones para imitar el uso de separar archivo cpp pero en la práctica si usted tiene la intención de escribir una plantilla de biblioteca de clases y distribuir con encabezado y archivos lib para ocultar la aplicación, simplemente no es posible.

    Saber por qué, veamos el proceso de compilación. Los archivos de encabezado nunca son compilados. Sólo son preprocesadas. El preprocesado de código es luego golpeado con el archivo cpp que en realidad es compilado. Ahora bien, si el compilador tiene que generar el adecuado diseño de memoria para el objeto se necesita saber el tipo de datos de la plantilla de clase.

    Realidad, se debe entender que la plantilla de clase no es una clase, sino una plantilla para una clase de la declaración y la definición de lo que es generado por el compilador en tiempo de compilación después de recibir la información del tipo de datos del argumento. Mientras el diseño de memoria no puede ser creado, las instrucciones para la definición de método no puede ser generado. Recuerde que el primer argumento del método de clase es el ‘este’ operador. Todos los métodos de la clase se convierten en métodos individuales con nombre de destrozarlo y el primer parámetro es el objeto el que se opera. El ‘este’ argumento es que en realidad dice sobre el tamaño de los objetos que en caso de la clase de plantilla no está disponible para el compilador, a menos que el usuario crea una instancia del objeto con un válido argumento de tipo. En este caso si pones las definiciones de método por separado en un archivo cpp y tratar de compilar el archivo de objeto en sí mismo no será generada con la información de la clase. La compilación no fallan, se tendría que generar el archivo de objeto, pero no va a generar ningún código para la clase de plantilla en el archivo objeto. Esta es la razón por la que el enlazador no puede encontrar los símbolos en los archivos objeto y la compilación falla.

    Ahora, ¿cuál es la alternativa para ocultar los detalles de implementación importantes? Como todos sabemos, el objetivo principal detrás de la separación de la interfaz de la aplicación es ocultar los detalles de implementación en forma binaria. Aquí es donde usted debe separar las estructuras de datos y algoritmos. Su plantilla de clases debe representar sólo las estructuras de datos no de los algoritmos. Esto permite que usted oculte más valiosos detalles de la implementación en independiente no templatized bibliotecas de clases, las clases dentro de la que iba a trabajar en la plantilla de clases o simplemente utilizarlos para mantener los datos. La clase de plantilla en realidad iba a contener menos código para asignar, get y set de datos. Resto de la obra sería realizada por el algoritmo de clases.

    Espero que esta discusión sería útil.

    • «se debe entender que la plantilla de clase no es una clase en todos» – ¿no era al revés? Plantilla de clase es una plantilla. «La plantilla de clase» se utiliza a veces en lugar de «la creación de instancias de una plantilla», y sería una clase real.
    • Sólo para referencia, no es correcto decir que no hay soluciones! La separación de las Estructuras de Datos de métodos también es una mala idea, ya que se oponen a la encapsulación. Hay una gran solución que puede utilizar en algunas situaciones (creo que más) aquí: stackoverflow.com/questions/495021/…
    • estás en lo correcto. Técnicamente «Plantilla de Clase» es lo que usted escribe, de modo que usted puede crear una instancia de una «Clase de Plantilla» y su correspondiente objeto. Sin embargo, yo creo que en una terminología genérica, el uso de ambos términos indistintamente podría no ser del todo mal, la sintaxis para la definición de la «Plantilla de Clase» en sí comienza con la palabra «plantilla» y no «clase».
    • Yo no he dicho que no hay soluciones. De hecho, todos los que están disponibles son soluciones sólo para imitar la separación de la interfaz y de implementación en el caso de las clases de plantilla. Ninguna de estas soluciones funcionan sin crear instancias de un determinado escrito de la plantilla de clase. Que de todos modos se disuelve el punto de genericity de la utilización de las plantillas de clase. La separación de las estructuras de datos de los algoritmos no es la misma que la separación de las estructuras de datos a partir de los métodos. Datos de la estructura de clases puede muy bien haber métodos como el de los constructores, los getters y setters.
  2. 83

    Se es posible, siempre y cuando usted sabe qué casos usted va a necesitar.

    Agregue el código siguiente al final de stack.cpp y va a trabajar :

    template class stack<int>;

    Todos los no-métodos de plantilla de la pila será instanciado, y la vinculación de paso funcionará bien.

    • En la práctica, la mayoría de las personas que utilizan un archivo cpp para esto – algo así como stackinstantiations.cpp.
    • ¿puede dar un ejemplo de lo que stackinstantiations.cpp sería así?
    • En realidad hay otras soluciones: codeproject.com/Articles/48575/…
    • Tengo un error error: se esperaba incondicional-id antes ‘;’ token de plantilla de la pila<int>; ¿sabes por qué? Gracias!
    • Se puede especificar el medio ambiente (compilador) y el código que usted está tratando de compilar ?
    • Feliz dando Gracias 🙂 yo estoy usando ubuntu y gcc 4.8.1. Yo copie el código anterior de exscape, y añadir «de la plantilla de la pila<int>;» al final de stack.cpp. Gracias.
    • gracias, tú también ! ¿Me puedes mostrar el contenido completo de stack.cpp ? Primeras preguntas que vienen a la mente son : ¿ha incluido <pila> y han tomado el cuidado de espacio de nombres std de pila ?
    • mi código es igual a la de los códigos de «@exscape», la única diferencia es que añadir «de la plantilla de la pila<int>;» al final de stack.cpp. #include <iostream> #include «de la pila.hpp de la plantilla» <nombre de Tipo> pila<Tipo>::pila() { std::cerr << «Hola, pila» << esto << «!» << std::endl; } template <nombre de Tipo> pila<Tipo>::~pila() { std::cerr << «Adiós, pila» << esto << «.» << std::endl; } la plantilla de la pila<int>;
    • +1 Y para las personas que quieren aprender más: cplusplus.com/forum/articles/14272
    • En realidad, la sintaxis correcta es template class stack<int>;.
    • Gracias Pablo. Yo no puedo creer que esta (estúpido) error mío había atrapado para siempre !

  3. 8

    Usted puede hacerlo de esta manera

    //xyz.h
    #ifndef _XYZ_
    #define _XYZ_
    
    template <typename XYZTYPE>
    class XYZ {
      //Class members declaration
    };
    
    #include "xyz.cpp"
    #endif
    
    //xyz.cpp
    #ifdef _XYZ_
    //Class definition goes here
    
    #endif

    Esto ha sido discutido en Daniweb

    También en Preguntas frecuentes pero el uso de C++ exportación de palabras clave.

    • includeing un cpp archivo es generalmente una idea terrible. incluso si usted tiene una razón válida para ello, el archivo – que en realidad es sólo una glorificado en el encabezado debe dar un hpp o de otra extensión (por ejemplo,tpp) dejar muy en claro lo que está pasando, eliminar la confusión en torno a makefiles orientación real cpp archivos, etc.
    • Podría usted explicar por qué incluso un .cpp archivo es una idea terrible?
    • debido a la extensión de cpp (o cc, o c, o lo que sea) indica que el archivo es un pedazo de la aplicación, que el resultado de la unidad de traducción (preprocesador de salida) es compilable por separado, y que el contenido del archivo se compila una vez sólo. esto no indica que el archivo es una parte reutilizable de la interfaz, de forma arbitraria en ninguna parte. #includeing un real cpp archivo rápidamente llenar la pantalla con múltiples errores de definición, y con razón. en este caso, como no es una razón para #include que, cpp fue sólo una mala elección de la extensión.
    • Así que, básicamente, es incorrecto usar el .cpp extensión para tal uso. Pero para utilizar otro decir .tpp está completamente bien, que iba a servir el mismo propósito, pero el uso de una extensión diferente para que sea más fácil/rápido entendimiento?
    • Sí, cpp/cc/etc debe ser evitado, pero es una buena idea utilizar algo distinto de hpp – por ejemplo,tpp, tcc, etc. – así que usted puede reutilizar el resto del nombre del archivo e indicar que el tpp archivo, aunque actúa como un encabezado, sostiene el fuera-de-línea de aplicación de la plantilla de las declaraciones en el correspondiente hpp. Así que este post comienza con una buena premisa – la separación de las declaraciones y definiciones 2 archivos diferentes, que pueden ser más fáciles de asimilar/grep o a veces es necesario debido a las dependencias circulares IME – pero luego termina mal por lo que sugiere que el 2º archivo tiene una extensión incorrecta
    • Yo recomendaría para este uso .inl extensión en lugar de .cpp uno. La mayoría de IDE ya están configurados para utilizar .inl como C/C++ encabezado. Así que usted mantenga sus declaraciones .h y sus implementaciones en el .inl

  4. 6

    No, no es posible. No sin la export palabra clave, que, para todos los intentos y propósitos no existe realmente.

    Lo mejor que puedes hacer es poner sus implementaciones de función en una «.tcc» o «.tpp» archivo, y #incluir el .tcc archivo al final de su .ch archivo. Sin embargo, esto es meramente estética; es la misma como en la implementación de todo en archivos de encabezado. Esto es simplemente el precio que se paga por el uso de plantillas.

    • Tu respuesta no es correcta. Puede generar código a partir de una plantilla de clase en un archivo cpp, dado saber qué argumentos de plantilla a utilizar. Ver mi respuesta para obtener más información.
    • Esto simplemente no es verdad.
    • Cierto, pero esto viene con la grave restricción de la necesidad de actualizar el .archivo cpp y recompilar cada vez que se introduce un nuevo tipo que utiliza la plantilla, que probablemente no es lo que el OP tenía en mente.
  5. 3

    Creo que hay dos razones principales para tratar de separar con plantilla de código en un encabezado y un cpp:

    Uno de ellos es por mera elegancia. A todos nos gusta escribir código que es wasy para leer, administrar y es reutilizable más tarde.

    Otro es la reducción de los tiempos de compilación.

    Estoy actualmente (como siempre) de codificación de software de simulación en conjunción con OpenCL y nos gusta mantener el código, así que se puede ejecutar con el flotador (cl_float) o doble (cl_double) tipos según sea necesario dependiendo de HW capacidad. Ahora esto se hace usando un #define lo REAL en el principio del código, pero esto no es muy elegante. El cambio de precisión deseado requiere recompilar la aplicación. Ya no hay tipos en tiempo de ejecución, tenemos que vivir con esto por el momento. Por suerte OpenCL núcleos se compilan en tiempo de ejecución, y un simple sizeof(REAL) nos permite modificar el código del kernel en tiempo de ejecución en consecuencia.

    El problema mucho más grande es que aunque la aplicación es modular, cuando el desarrollo de las clases auxiliares (tales como aquellos que pre-calcular la simulación constantes) también tiene que ser de plantilla. Estas clases de aparecer al menos una vez en la parte superior de la clase de árbol de dependencias, como el final de la clase de plantilla de la Simulación se tiene un ejemplo de una de estas clases de fábrica, lo que significa que prácticamente cada vez que hago un cambio menor a la clase de fábrica, todo el software tiene que ser reconstruido. Esto es muy molesto, pero me parece no puede encontrar una solución mejor.

  6. 2

    A veces es posible tener más de una aplicación oculta en el archivo cpp, si se puede extraer la funcionalidad común foo todos los parámetros de la plantilla en la no-clase de plantilla (posiblemente de tipo inseguro). Entonces encabezado contendrá la redirección de llamadas a esa clase. Enfoque Similar se utiliza, cuando se lucha con la plantilla «inflar» problema.

    • +1, aunque no funciona muy bien, la mayoría de las veces (al menos, no tan a menudo como quiero)
  7. 2

    Si usted sabe lo que los tipos de la pila será utilizada con, usted puede crear instancias de las mismas expicitly en el archivo cpp, y mantener todos los elementos pertinentes del código de allí.

    También es posible la exportación de estos a través de los archivos Dll (!) pero es muy difícil conseguir que la sintaxis de la derecha (MS-combinaciones específicas de __declspec(dllexport) y la exportación de palabras clave).

    Que hemos utilizado a lo que en matemáticas/geometría lib que con plantilla de doble/float, pero tenía un buen montón de código. (Busqué en google alrededor de ella en el tiempo, no tienen de que el código de hoy, sin embargo.)

  8. 2

    El problema es que una plantilla no generar una clase real, es sólo un plantilla indica al compilador cómo generar una clase. Se necesita generar una clase concreta.

    El modo fácil y natural es poner los métodos en el archivo de encabezado. Pero hay otra manera.

    En su .archivo cpp, si usted tiene un de referencia para cada ejecución de plantilla y el método que usted requiera, el compilador generará ellos para el uso de todo su proyecto.

    nuevo stack.cpp:

    #include <iostream>
    #include "stack.hpp"
    template <typename Type> stack<Type>::stack() {
            std::cerr << "Hello, stack " << this << "!" << std::endl;
    }
    template <typename Type> stack<Type>::~stack() {
            std::cerr << "Goodbye, stack " << this << "." << std::endl;
    }
    static void DummyFunc() {
        static stack<int> stack_int;  //generates the constructor and destructor code
        //... any other method invocations need to go here to produce the method code
    }
    • Usted no necesita la dummey función: Uso ‘de la plantilla de la pila<int>;’ Esto obliga a un instanciation de la plantilla en la actual unidad de compilación. Muy útil si se define una plantilla, pero sólo quiero un par de implementaciones específicas en un lib compartido.
    • incluyendo todas las funciones de miembro? Eso es fantástico. Usted debe agregar esta propuesta a los «ocultos características de C++» hilo.
    • He encontrado un artículo sobre esto en caso de que alguien quiera aprender más: cplusplus.com/forum/articles/14272
  9. 1

    Que usted necesita para tener todo en el hpp de archivo. El problema es que las clases no son en realidad creado hasta el compilador ve que ellos son necesarios por algún OTRO archivo cpp – por lo que tiene que tener todo el código disponible para compilar la clase de plantilla en ese momento.

    Una cosa que me suelen hacer es tratar de dividir mis plantillas en un genérico sin plantilla (que se puede dividir entre cpp/hpp) y el tipo específico de parte de la plantilla de la que hereda la no-clase de plantilla.

  10. 1

    Sólo si #include "stack.cpp al final de stack.hpp. Me gustaría recomendar este enfoque, si la ejecución es relativamente grande, y si cambia el nombre de la .archivo cpp a otra extensión, como para diferenciarlos de regular de código.

    • Si usted está haciendo esto, usted querrá agregar #ifndef STACK_CPP (y amigos) para su stack.cpp archivo.
    • Me golpearon a esta sugerencia. Yo también prefiero este enfoque para el estilo de razones.
    • Sí, en tal caso, el 2º archivo definitivamente no debe ser dada la extensión de cpp (o cc o lo que sea), porque es un marcado contraste con su papel real. Se debería ser dado a una extensión diferente que indica que (A) un encabezado y (B) un encabezado para ser incluido en el abajo de otro encabezado. Yo uso tpp por esto, lo que fácilmente puede también soporte para tempfinales de implementation (fuera de la línea de las definiciones). Yo divagaba más sobre esto aquí: stackoverflow.com/questions/1724036/…
  11. 0

    Debido a que las plantillas se compilan cuando sea necesario, esto obliga a una restricción para multi-archivo de los proyectos: la aplicación (definición) de una plantilla de clase o función debe estar en el mismo archivo de su declaración. Eso significa que no podemos separar la interfaz en un archivo de cabecera aparte, y que debemos incluir tanto la interfaz y la implementación de cualquier archivo que utiliza las plantillas.

  12. 0

    Otra posibilidad es hacer algo como:

    #ifndef _STACK_HPP
    #define _STACK_HPP
    
    template <typename Type>
    class stack {
        public:
                stack();
                ~stack();
    };
    
    #include "stack.cpp"  //Note the include.  The inclusion
                          //of stack.h in stack.cpp must be 
                          //removed to avoid a circular include.
    
    #endif

    No me gusta esta sugerencia como una cuestión de estilo, pero es posible que se adapte a usted.

    • La glorificado 2º encabezado se incluye al menos debe tener una extensión distinta de cpp para evitar la confusión con real los archivos de origen. Sugerencias comunes incluyen tpp y tcc.
  13. 0

    La ‘exportación’ de la palabra clave es la forma de separar la plantilla de la implementación de la declaración de la plantilla. Este fue introducido en el estándar de C++ sin una implementación existente. En su debido momento, sólo un par de compiladores realmente implementado. Leer información en profundidad en Informar QUE el artículo de la exportación

    • Esto es casi un enlace única respuesta, y que el enlace está muerto.
  14. 0

    1) Recordar la razón principal para separar .h and .archivos cpp es ocultar la implementación de la clase como una compilados por separado Obj código que pueden estar vinculados con el código de usuario que incluye una .h de la clase.

    2) No clases de plantilla tienen todas las variables de forma concreta y definida específicamente en .h and .archivos cpp. Por lo que el compilador tiene la necesidad de información acerca de todos los tipos de datos utilizados en la clase antes de la compilación, de la traducción  generar el objeto/código de máquina
    Clases de plantilla no tiene ninguna información sobre el tipo de datos específico antes de que el usuario de la clase instanciar un objeto de pasar los datos requeridos tipo:

            TClass<int> myObj;

    3) Sólo después de esto la creación de instancias, el compilador generará la versión específica de la clase de plantilla para que coincida con el pasado de tipo de datos(s).

    4) por lo Tanto, .cpp NO Puede ser compilado por separado, sin conocer a los usuarios el tipo de datos específico. Por lo que ha de permanecer como el código fuente dentro «.h» hasta que el usuario especifique los datos requeridos tipo luego, puede ser generada a un tipo de datos específico, a continuación, compila

  15. -3

    Estoy trabajando con Visual studio 2010, si desea dividir sus archivos .h and .cpp, incluir su cpp cabecera en la final de la .h archivo

Dejar respuesta

Please enter your comment!
Please enter your name here