usando una plantilla externa (C++11) para evitar la creación de instancias

Resuelto codekiddy asked hace 12 años • 5 respuestas

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 templateo 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 templatepalabra clave para evitar múltiples instancias iguales. ¿Esto es solo para clases o funciones también?

codekiddy avatar Nov 15 '11 08:11 codekiddy
Aceptado

Solo debe usar extern templatepara 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 externpalabra 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.cppa:

// 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 externen 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.

Daniel avatar Nov 15 '2011 03:11 Daniel

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.

sehe avatar Nov 15 '2011 07:11 sehe