Múltiples definiciones de una plantilla de función
Pregunta
Suponga que un archivo de encabezado define una plantilla de función. Ahora suponga que dos archivos de implementación #include
este encabezado, y cada uno de ellos tiene una llamada a la plantilla de función. En ambos archivos de implementación, la plantilla de función está instanciada con el mismo tipo.
// header.hh
template <typename T>
void f(const T& o)
{
// ...
}
// impl1.cc
#include "header.hh"
void fimpl1()
{
f(42);
}
// impl2.cc
#include "header.hh"
void fimpl2()
{
f(24);
}
Uno puede esperar que el enlazador se queje de múltiples definiciones de f ()
. Específicamente, si f ()
no fuera una plantilla, ese sería el caso.
- ¿Cómo es que el enlazador no se queja de múltiples definiciones de
f ()
? - ¿Se especifica en el estándar que el enlazador debe manejar esta situación con gracia? En otras palabras, ¿puedo contar siempre con programas similares a los anteriores para compilar y vincular?
- Si el enlazador puede ser lo suficientemente inteligente como para desambiguar un conjunto de instancias de plantillas de funciones, ¿por qué no puede hacer lo mismo para las funciones regulares, dado que son idénticas como es el caso de las plantillas de funciones instanciadas?
Solución
Para admitir C ++, el enlazador es lo suficientemente inteligente como para reconocer que todas tienen la misma función y descarta todas menos una.
EDITAR: aclaración: El vinculador no compara los contenidos de las funciones y determina que son iguales. Las funciones programadas están marcadas como tales y el vinculador reconoce que tienen las mismas firmas.
Otros consejos
El manual del compilador Gnu C ++ tiene una buena discusión de esto . Un extracto:
Las plantillas de C ++ son el primer idioma Característica para requerir más inteligencia. del medio ambiente que uno usualmente encuentra en un sistema UNIX. De alguna manera el compilador y enlazador tienen que asegurarse que se produce cada instancia de plantilla exactamente una vez en el ejecutable si Es necesario, y no de otra manera. Hay dos enfoques básicos para esto problema, que se conoce como Modelo Borland y el modelo Cfront.
modelo Borland
Borland C ++ resolvió la plantilla problema de instanciación agregando el Código equivalente de bloques comunes a su enlazador el compilador emite instancias de plantilla en cada traducción unidad que los usa, y el enlazador los colapsa juntos. La ventaja De este modelo es que solo el enlazador. tiene que considerar los archivos objeto sí mismos; no hay externo La complejidad de la que preocuparse. Esta desventaja es ese tiempo de compilación se incrementa porque el código de la plantilla se está compilando repetidamente Código escrito para este modelo tiende a incluir definiciones de todas las plantillas en el archivo de cabecera, ya que deben estar visto para ser instanciado.
Modelo Cfront
El traductor AT & amp; T C ++, Cfront, resolvió la instanciación de plantilla problema creando la noción de un repositorio de plantillas, automáticamente lugar mantenido donde la plantilla Las instancias son almacenadas. Un más moderno la versión del repositorio funciona como Sigue: Como archivos de objetos individuales. Se construyen, el compilador coloca cualquier definiciones de plantillas y instancias encontradas en el repositorio. En el momento del enlace, el enlace el contenedor agrega los objetos en el repositorio y compila cualquier necesidad instancias que no eran previamente emitido Las ventajas de este modelo. son una velocidad de compilación más óptima y la capacidad de utilizar el enlazador del sistema; para implementar el modelo de Borland a proveedor compilador también necesita reemplazar el enlazador Las desventajas son complejidad enormemente aumentada, y por lo tanto potencial de error; por algún código esto puede ser igual de transparente, pero en la práctica puede ser muy difícil para construir múltiples programas en uno Directorio y un programa en múltiples directorios. Código escrito para esto modelo tiende a separar definiciones de plantillas de miembros no en línea en una archivo separado, que debería ser compilado por separado.
Cuando se usa con GNU ld versión 2.8 o más tarde en un sistema ELF como GNU / Linux o Solaris 2, o en Microsoft Windows, G ++ es compatible con Modelo de Borland. En otros sistemas, G ++ no implementa ninguno de los modelos automáticos.
Esto es más o menos un caso especial solo para plantillas.
El compilador solo genera las instancias de plantilla que realmente se utilizan. Como no tiene control sobre qué código se generará a partir de otros archivos de origen, debe generar el código de la plantilla una vez para cada archivo, para asegurarse de que el método se genere en absoluto.
Dado que es difícil resolver esto (el estándar tiene una palabra clave extern
para las plantillas, pero g ++ no lo implementa) el enlazador simplemente acepta las definiciones múltiples.