Figura 1: plantillas de funciones
TemplHeader.h
template<typename T>
void f();
TemplCpp.cpp
template<typename T>
void f(){
//...
}
//explicit instantation
template void f<T>();
Main.cpp
#include "TemplHeader.h"
extern template void f<T>(); //is this correct?
int main() {
f<char>();
return 0;
}
¿Es esta la forma correcta de usar extern template
, o utilizo esta palabra clave solo para plantillas de clase como en la Figura 2?
Figura 2: plantillas de clases
TemplHeader.h
template<typename T>
class foo {
T f();
};
TemplCpp.cpp
template<typename T>
void foo<T>::f() {
//...
}
//explicit instantation
template class foo<int>;
Main.cpp
#include "TemplHeader.h"
extern template class foo<int>();
int main() {
foo<int> test;
return 0;
}
Sé que es bueno poner todo esto en un archivo de encabezado, pero si instanciamos plantillas con los mismos parámetros en varios archivos, obtendremos varias definiciones iguales y el compilador las eliminará todas (excepto una) para evitar errores. ¿Cómo lo uso extern template
? ¿Podemos usarlo solo para clases, o podemos usarlo también para funciones?
Además, la Figura 1 y la Figura 2 pueden expandirse a una solución donde las plantillas están en un solo archivo de encabezado. En ese caso, necesitamos usar la extern template
palabra clave para evitar múltiples instancias iguales. ¿Es esto solo para clases o funciones también?
extern template class foo<int>();
parece un error.()
de la línea externa. tanto su libro como visual studio están equivocados, intente usar un compilador compatible con más estándares como g ++ o clang y verá el problema.Respuestas:
Solo debe usar
extern template
para forzar al compilador a no instanciar una plantilla cuando sepa que se instanciará en otro lugar. Se utiliza para reducir el tiempo de compilación y el tamaño del archivo objeto.Por ejemplo:
Esto dará como resultado los siguientes archivos de objeto:
Si ambos archivos están vinculados, uno
void ReallyBigFunction<int>()
se descartará, lo que resultará en una pérdida de tiempo de compilación y tamaño del archivo objeto.Para no perder tiempo de compilación y tamaño de archivo de objeto, hay una
extern
palabra clave que hace que el compilador no compile una función de plantilla. Debe usar esto si y solo si sabe que se usa en el mismo binario en otro lugar.Cambiar
source2.cpp
a:Dará como resultado los siguientes archivos de objeto:
Cuando ambos estén vinculados, el segundo archivo de objeto solo usará el símbolo del primer archivo de objeto. No hay necesidad de descartar ni perder tiempo de compilación ni tamaño de archivo de objeto.
Esto solo debe usarse dentro de un proyecto, como en los momentos en que usa una plantilla como
vector<int>
varias veces, debe usarlaextern
en todos los archivos fuente menos uno.Esto también se aplica a las clases y la función como una, e incluso a las funciones de miembro de la plantilla.
fuente
Wikipedia tiene la mejor descripción
La advertencia:
nonstandard extension used...
Microsoft VC ++ solía tener una versión no estándar de esta función desde hace algunos años (en C ++ 03). El compilador advierte sobre eso para evitar problemas de portabilidad con el código que también debe compilarse en diferentes compiladores.
Mire la muestra en la página vinculada para ver que funciona aproximadamente de la misma manera. Puede esperar que el mensaje desaparezca con futuras versiones de MSVC, excepto, por supuesto, cuando se utilizan otras extensiones de compilador no estándar al mismo tiempo.
fuente
std::vector
(bastante seguro que todos lo hacen),extern
no tiene ningún efecto.extern template
solo es necesario si la plantilla de declaración está completaEsto se insinuó en otras respuestas, pero no creo que se le haya dado suficiente énfasis.
Lo que esto significa es que en los ejemplos de OP,
extern template
no tiene ningún efecto porque las definiciones de plantilla en los encabezados estaban incompletas:void f();
: solo declaración, sin cuerpoclass foo
: declara el métodof()
pero no tiene definiciónPor lo tanto, recomendaría simplemente eliminar la
extern template
definición en ese caso particular: solo necesita agregarlas si las clases están completamente definidas.Por ejemplo:
TemplHeader.h
TemplCpp.cpp
Main.cpp
compilar y ver símbolos con
nm
:salida:
y luego
man nm
vemos queU
significa indefinido, por lo que la definición se mantuvo soloTemplCpp
como se deseaba.Todo esto se reduce a la compensación de declaraciones de encabezado completas:
extern template
cada includer, lo que los programadores probablemente olvidarán hacer.Se muestran más ejemplos de estos en: Creación de instancias de plantilla explícita : ¿cuándo se usa?
Dado que el tiempo de compilación es tan crítico en proyectos grandes, recomendaría encarecidamente declaraciones de plantilla incompletas, a menos que las partes externas necesiten absolutamente reutilizar su código con sus propias clases personalizadas complejas.
Y en ese caso, primero trataría de usar polimorfismo para evitar el problema del tiempo de compilación, y solo usaría plantillas si se pueden lograr mejoras notables en el rendimiento.
Probado en Ubuntu 18.04.
fuente
El problema conocido con las plantillas es el exceso de código, que es consecuencia de generar la definición de clase en todos y cada uno de los módulos que invoca la especialización de la plantilla de clase. Para evitar esto, comenzando con C ++ 0x, se podría usar la palabra clave extern frente a la especialización de la plantilla de clase
La instancia explícita de la clase de plantilla debe ocurrir solo en una sola unidad de traducción, preferiblemente la que tiene la definición de plantilla (MyClass.cpp)
fuente
Si ha utilizado extern para funciones antes, se sigue exactamente la misma filosofía para las plantillas. si no es así, puede ser útil utilizar extern para funciones simples. Además, es posible que desee poner el (los) externo (s) en el archivo de encabezado e incluir el encabezado cuando lo necesite.
fuente