usando una plantilla externa (C++11) para evitar la creación de instancias
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>();
principal.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 uso esta palabra clave solo para plantillas de clase como en la Figura 2?
Figura 2: plantillas de clase
TemplHeader.h
template<typename T>
class foo {
T f();
};
TemplCpp.cpp
template<typename T>
void foo<T>::f() {
//...
}
//explicit instantation
template class foo<int>;
principal.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 creamos instancias de 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 también podemos usarlo para funciones?
Además, la Figura 1 y la Figura 2 se pueden ampliar a una solución donde las plantillas estén en un único archivo de encabezado. En ese caso, necesitamos usar la extern template
palabra clave para evitar múltiples instancias iguales. ¿Esto es solo para clases o funciones también?
Solo debe usar extern template
para forzar al compilador a no crear una instancia de una plantilla cuando sepa que se creará una instancia en otro lugar. Se utiliza para reducir el tiempo de compilación y el tamaño del archivo de objeto.
Por ejemplo:
// header.h
template<typename T>
void ReallyBigFunction()
{
// Body
}
// source1.cpp
#include "header.h"
void something1()
{
ReallyBigFunction<int>();
}
// source2.cpp
#include "header.h"
void something2()
{
ReallyBigFunction<int>();
}
Esto dará como resultado los siguientes archivos de objetos:
source1.o
void something1()
void ReallyBigFunction<int>() // Compiled first time
source2.o
void something2()
void ReallyBigFunction<int>() // Compiled second time
Si ambos archivos están vinculados, uno void ReallyBigFunction<int>()
se descartará, lo que provocará una pérdida de tiempo de compilación y tamaño del archivo objeto.
Para no perder tiempo de compilación y tamaño del archivo objeto, hay una extern
palabra clave que hace que el compilador no compile una función de plantilla. Deberías usar esto si y sólo si sabes que se usa en el mismo binario en otro lugar.
Cambiando source2.cpp
a:
// source2.cpp
#include "header.h"
extern template void ReallyBigFunction<int>();
void something2()
{
ReallyBigFunction<int>();
}
Dará como resultado los siguientes archivos de objetos:
source1.o
void something1()
void ReallyBigFunction<int>() // compiled just one time
source2.o
void something2()
// No ReallyBigFunction<int> here because of the extern
Cuando ambos estén vinculados, el segundo archivo objeto simplemente usará el símbolo del primer archivo objeto. No es necesario descartarlo ni perder tiempo de compilación ni tamaño de archivo de objeto.
Esto solo debe usarse dentro de un proyecto, como cuando usa una plantilla vector<int>
varias veces, debe usarla extern
en todos los archivos fuente menos uno.
Esto también se aplica a clases y funciones como una sola, e incluso a funciones miembro de plantilla.
Wikipedia tiene la mejor descripción.
En C++03, el compilador debe crear una instancia de una plantilla siempre que se encuentre una plantilla completamente especificada en una unidad de traducción. Si se crean instancias de la plantilla con los mismos tipos en muchas unidades de traducción, esto puede aumentar drásticamente los tiempos de compilación. No hay forma de evitar esto en C++03, por lo que C++11 introdujo declaraciones de plantilla externas, análogas a las declaraciones de datos externas.
C++03 tiene esta sintaxis para obligar al compilador a crear una instancia de una plantilla:
template class std::vector<MyClass>;
C++ 11 ahora proporciona esta sintaxis:
extern template class std::vector<MyClass>;
lo que le dice al compilador que no cree una instancia de la plantilla en esta unidad de traducción.
La advertencia:nonstandard extension used...
Microsoft VC++ solía tener una versión no estándar de esta característica desde hace algunos años (en C++03). El compilador advierte sobre esto para evitar problemas de portabilidad con el código que también debe compilarse en diferentes compiladores.
Mire el ejemplo en la página vinculada para ver que funciona más o menos de la misma manera. Puede esperar que el mensaje desaparezca con futuras versiones de MSVC, excepto, por supuesto, cuando utilice otras extensiones de compilador no estándar al mismo tiempo.