2008-11-10 17 views

Respuesta

22

Aunque el estándar C++ no tiene tal requisito, algunos compiladores requieren que todas las plantillas de funciones estén disponibles en cada unidad de traducción en la que se utilicen. En efecto, para esos compiladores, los cuerpos de las funciones de plantilla deben estar disponibles en un archivo de encabezado Para repetir: eso significa que esos compiladores no permitirán que se definan en archivos sin encabezado, como archivos .cpp.Para aclarar, en C++ ESE Esto significa que este:

// ORIGINAL version of xyz.h 
template <typename T> 
struct xyz 
{ 
    xyz(); 
    ~xyz(); 
}; 

NO sería satisfecha con estas definiciones de la ctor y dtors:

// ORIGINAL version of xyz.cpp 
#include "xyz.h" 

template <typename T> 
xyz<T>::xyz() {} 

template <typename T> 
xyz<T>::~xyz() {} 

porque usarlo:

// main.cpp 
#include "xyz.h" 

int main() 
{ 
    xyz<int> xyzint; 

    return 0; 
} 

producirá un error. Por ejemplo, con Comeau C++ se obtendría:

C:\export>como xyz.cpp main.cpp 
C++'ing xyz.cpp... 
Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86 
Copyright 1988-2004 Comeau Computing. All rights reserved. 
MODE:non-strict warnings microsoft C++ 

C++'ing main.cpp... 
Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86 
Copyright 1988-2004 Comeau Computing. All rights reserved. 
MODE:non-strict warnings microsoft C++ 

main.obj : error LNK2001: unresolved external symbol xyz<T1>::~xyz<int>() [with T1=int] 
main.obj : error LNK2019: unresolved external symbol xyz<T1>::xyz<int>() [with T1=int] referenced in function _main 
aout.exe : fatal error LNK1120: 2 unresolved externals 

porque no hay uso de la ctor o dtor dentro xyz.cpp, por lo tanto, no hay instancias que tiene que producirse a partir de ahí. Para bien o para mal, así es como funcionan las plantillas.

Una forma de evitar esto es solicitar explícitamente la creación de instancias de xyz, en este ejemplo de xyz<int>. En un esfuerzo de fuerza bruta, esto podría ser añadido a xyz.cpp mediante la adición de esta línea en el extremo de la misma:

template xyz<int>; 

que solicitan que (todos) xyz<int> ser instanciada. Sin embargo, eso está en el lugar equivocado, ya que significa que cada vez que se genera un nuevo tipo xyz se debe modificar el archivo de implementación xyz.cpp. Una manera menos intrusiva para evitar que el archivo es crear otro:

// xyztir.cpp 
#include "xyz.cpp" // .cpp file!!!, not .h file!! 

template xyz<int>; 

Esto es aún un poco dolorosa, ya que todavía requiere una intervención manual cada vez que un nuevo XYZ se dio a luz. En un programa no trivial esto podría ser una demanda de mantenimiento irracional.

Así que en vez, otra manera de abordar esto es #include "xyz.cpp" en el extremo del xyz.h:

// xyz.h 

// ... previous content of xyz.h ... 

#include "xyz.cpp" 

Se podría, por supuesto, literalmente, traer (cortar y pegar) el contenido de xyz.cpp a la final de xyz.h, por lo tanto, deshacerse de xyz.cpp; se trata de una organización de archivos y al final los resultados del preprocesamiento serán los mismos, ya que los cuerpos ctor y dtor estarán en el encabezado y, por lo tanto, se incluirán en cualquier solicitud de compilación, ya que eso usaría el encabezado respectivo. De cualquier manera, esto tiene el efecto secundario de que ahora cada plantilla está en su archivo de encabezado. Podría ralentizar la compilación y podría provocar una inflamación del código. Una forma de abordar este último es declarar las funciones en cuestión, en este caso el ctor y dtor, como en línea, por lo que esto requeriría que modifique xyz.cpp en el ejemplo en ejecución.

Como un lado, algunos compiladores también requieren que algunas funciones se definan en línea dentro de una clase, y no fuera de una, por lo que la configuración anterior debería modificarse aún más en el caso de esos compiladores. Tenga en cuenta que este es un problema del compilador, no uno de Standard C++, por lo que no todos los compiladores lo requieren. Por ejemplo, Comeau C++ no lo hace, ni debería hacerlo. Consulte http://www.comeaucomputing.com/4.0/docs/userman/ati.html para obtener detalles sobre nuestra configuración actual. En resumen, Comeau C++ es compatible con muchos modelos, incluido uno que se acerca a las intenciones de las palabras clave de exportación (como una extensión), e incluso soporta la exportación en sí misma.

Por último, tenga en cuenta que la palabra clave de exportación C++ está destinada a aliviar la pregunta original. Sin embargo, actualmente Comeau C++ es el único compilador que se publica para admitir la exportación. Consulte http://www.comeaucomputing.com/4.0/docs/userman/export.html y http://www.comeaucomputing.com/4.3.0/minor/win95+/43stuff.txt para obtener más información. Esperemos que a medida que otros compiladores alcancen el estándar C++, esta situación cambie.En el ejemplo anterior, utilizando la exportación significa volver al código original que produjo los errores del enlazador, y hacer un cambio: declarar la plantilla en xyz.h con la palabra clave de exportación:

// xyz.h 

export 
// ... ORIGINAL contents of xyz.h ... 

El ctor y DTOR en xyz. cpp se exportará simplemente en virtud de #includeing xyz.h, que ya lo hace. Entonces, en este caso no necesita xyztir.cpp, ni la solicitud de instanciación al final de xyz.cpp, y no necesita el ctor o dtor ingresados ​​manualmente en xyz.h. Con la línea de comandos mostrada anteriormente, es posible que el compilador lo haga todo automáticamente.

+1

Nota [Respuesta de Greg Rogers] (http://stackoverflow.com/a/279617/481061) y los comentarios allí: la palabra clave 'export' se eliminó del estándar y probablemente nunca se implementará en otro compilador. –

6

Ver this explicación para su uso

Bastantes compiladores no lo soportan, ya sea porque es demasiado nuevo o en el caso de gcc - porque disaprove.

Este artículo describe el soporte estándar para muchos compiladores. Visual Studio support for new C/C++ standards?

+1

¡No es demasiado nuevo, tiene 10 años como las otras características en el estándar C++ 98! : D Es más que implementarlo requiere un rediseño del compilador, y no creen que valga la pena. – KTC

+3

Hace que el enlazador sea complicado, especialmente si desea hacer muchas optimizaciones inteligentes del programa completo en la etapa de enlace –

1

Los únicos compiladores que admiten las plantillas exportadas en el momento (por lo que yo sé) son Comeau, el que vino con Borland C++ Builder X pero no la corriente C++ Builder, e Intel (al menos extraoficialmente, si no oficialmente, no estoy seguro).

4

En pocas palabras:

export le permite despegar la declaración (es decir cabecera.) De la definición (es decir, el código.) Al escribir sus clases de plantilla. Si el compilador no es compatible con export, debe colocar la declaración y la definición en un solo lugar.

5

Ver here y here para el tratamiento de Herb Sutter del tema.

Básicamente: export se ha implementado en el compilador solo one - y en esa implementación, exportar realmente aumenta el acoplamiento entre la definición de plantilla y la declaración, mientras que el único punto al introducir la exportación fue disminuir este acoplamiento.

Es por eso que la mayoría de los compiladores no se molestan. Pensé que simplemente habrían eliminado la exportación del lenguaje en C++ 0x, pero no creo que lo hayan hecho. Quizás algún día haya una buena forma de implementar exportaciones que tengan el uso previsto.

+1

Herb Sutter es un empleado de Microsoft. Ha intentado eliminar la exportación de C++ 0x y ha fallado. Entonces, su opinión podría ser un poco parcial. Divulgación: voté en contra de su propuesta a ISO. – MSalters

+0

¿Desea comentar por qué? Todo lo que he podido averiguar sobre esto implica que la característica es un problema muerto, permanezca o no en el estándar. –

+1

Sutter siendo un empleado de MS no tiene nada que ver con eso, solo piensa que la exportación no funcionó, por lo que creo que debería eliminarse. – KTC

4

Exportar es una característica que introduce una dependencia circular entre el enlazador y el compilador. Como otros señalaron, permite que una unidad de traducción contenga la definición de una plantilla utilizada en otra. El enlazador será el primero en detectar esto, pero necesita el compilador para la instanciación de la plantilla. Y esto implica un trabajo realmente duro, como la búsqueda de nombres.

Comeau lo presentó primero, hace aproximadamente 5 años IIRC. Funcionó bastante bien en la primera versión beta que obtuve. Incluso testcases como A < 2> usando B < 2> usando A < 1> usando B < 1> usando A < 0>, funcionó, si las plantillas A y B venían de diferentes TU. Claro, el enlazador repetidamente invocaba el compilador, pero todas las búsquedas de nombres funcionaban bien. Instanciación A < 1> nombres encontrados de A.cpp que eran invisibles en B.cpp.

Cuestiones relacionadas