tipos programa plantillas lenguaje funciones fuente ejemplos comandos codigos codigo basicos c++ templates header

plantillas - programa c++



¿Cómo conciliar el lenguaje de C++ de separar encabezado/fuente con plantillas? (5)

Aquí hay un par de técnicas que uso cuando escribo código de plantilla que mueven implementaciones en archivos cpp:

  1. Mueva partes de métodos que no dependen de los parámetros de plantilla en funciones auxiliares o clases de base independientes que no sean plantillas. Los cuerpos pueden ir a sus propios archivos cpp.

  2. Escribir declaraciones de especializaciones comunes. Las definiciones pueden vivir en sus propios archivos cpp.

Me pregunto un poco sobre este negocio de plantillas.

En C y C ++ es muy común colocar declaraciones en archivos de cabecera y definiciones en archivos fuente, y mantener los dos completamente separados. Sin embargo, esto ni siquiera parece posible (de ninguna manera) cuando se trata de plantillas, y como todos sabemos, las plantillas son una gran herramienta.

Además, Boost es principalmente encabezados, por lo que este es un problema real. ¿Sigue siendo una buena idea separar los encabezados y la fuente en C ++, o debería simplemente no confiar mucho en las plantillas?


Creo que lo realmente importante de comprender sobre las plantillas es que, parafraseando a Bjarne Stroustrop, C ++ es como muchos lenguajes en uno. Las convenciones y modismos de la creación de plantillas son diferentes a las de escribir C ++ "regular", casi como otro idioma.

Es absolutamente una buena idea separar los archivos de encabezado y de implementación en C ++ "regular", porque los archivos de encabezado le dicen al compilador lo que proporcionará como implementación más adelante (en el momento del enlace). Es importante porque esta separación es algo muy real en la mayoría de los sistemas operativos comunes: el tiempo de enlace puede ocurrir cuando el usuario ejecuta el programa. Puede compilar la implementación en binarios (so''s, dll) y enviar los encabezados inalterados para que los desarrolladores sepan cómo utilizar su implementación ahora opaca.

Ahora, para las plantillas, no puede hacer eso porque el compilador tiene que resolver completamente todo en tiempo de compilación . Es decir, cuando compila los encabezados, tienen que tener la implementación de la plantilla para que el compilador pueda darle sentido a sus encabezados. El compilador esencialmente "des-plantillas" de sus plantillas cuando compila, por lo que no hay ninguna opción para separar la interfaz y la implementación de la plantilla C ++ .

La separación de la interfaz y la implementación son buenas prácticas, en general, pero cuando deambulas por template-land, te diriges explícitamente a un lugar donde eso simplemente no es posible, porque eso es lo que significa una plantilla: resolver y construir la implementación de esta interfaz en compilación tiempo, no en tiempo de ejecución .


La creación de una instancia de una plantilla es costosa en tiempo de compilación pero virtual en tiempo de ejecución. Básicamente, cada vez que utiliza un nuevo tipo de plantilla, el compilador tiene que generar el código para ese nuevo tipo, es por eso que el código está en un encabezado, para que el compilador tenga acceso al código más adelante.

Poner todo tu código en .cpp permite al compilador compilar ese código solo una vez, lo que acelera enormemente la compilación. En teoría, podría escribir todo el código en los encabezados, funcionará bien, pero llevará mucho tiempo compilar proyectos muy grandes. Además, tan pronto como cambie una línea en cualquier lugar, tendrá que reconstruir todo.

Ahora puedes preguntar, ¿cómo es que STL y BOOST no son tan lentos? Ahí es donde los encabezados precompilados acuden al rescate. Los PCH permiten que el compilador haga el trabajo más costoso solo una vez. Esto funciona bien con un código que no cambiará a menudo como las bibliotecas, pero su efecto está totalmente anulado para el código que cambia mucho, ya que tendrá que volver a compilar todo el conjunto de encabezados precompilados cada vez. El compilador también usa un par de trucos para evitar recompilar todo el código de plantilla en cada unidad de compilación.

También tenga en cuenta que C ++ 0x introducirá mecanismos explícitos para controlar mejor la instanciación de plantillas. Podrá crear instancias explícitas de plantillas y, lo que es más importante, evitar la instanciación en algunas unidades de compilación. Sin embargo, la mayoría de ese trabajo ya está siendo realizado por la mayoría de los compiladores sin nuestro conocimiento.

Por lo tanto, la regla de oro es poner tanto código (e incluir directivas) como sea posible en su .cpp. Si no puedes, bueno, no puedes.

Mi consejo sería: no usar plantillas solo por gusto . Si tiene que crear una plantilla, tenga cuidado y tenga en cuenta que, de hecho, está eligiendo entre la velocidad de compilación y la facilidad de uso de la plantilla.


Mi favorito personal es esta estructura:

Archivo de cabecera:

#ifndef HEADER_FILE #define HEADER_FILE template < typename T > class X { void method(); }; #include "header_impl.h" #endif

Archivo de implementación:

#ifndef HEADER_IMPL_FILE #define HEADER_IMPL_FILE #include "header.h" template < typename T > void X<T>::method() { } #endif


Separar encabezado y fuente no es un modismo C ++, es más un modismo C, precisamente porque C ++ favorece el uso de plantillas y funciones en línea donde sea posible para reducir el tiempo de ejecución del programa.