2008-09-12 34 views
25

Supongamos que tengo fileA.h que declara una clase classA con función de plantilla SomeFunc<T>(). Esta función se implementa directamente en el archivo de encabezado (como es habitual para las funciones de plantilla). Ahora agrego una implementación especializada de SomeFunc() (como para SomeFunc<int>()) en fileA.C (es decir, no en el archivo de encabezado).Visibilidad de la plantilla de especialización de la función C++

Si ahora llamo al SomeFunc<int>() desde algún otro código (tal vez también desde otra biblioteca), ¿llamaría a la versión genérica o la especialización?

Tengo este problema en este momento, donde la clase y la función viven en una biblioteca que es utilizada por dos aplicaciones. Y una aplicación utiliza correctamente la especialización, mientras que otra usa la forma genérica (lo que causa problemas de tiempo de ejecución más adelante). ¿Por qué la diferencia? ¿Podría esto estar relacionado con las opciones del vinculador, etc.? Esto está en Linux, con g ++ 4.1.2.

Respuesta

0

A menos que la función de plantilla especializada también se encuentre en el archivo de encabezado, la otra aplicación no tendrá conocimiento de la versión especializada. La solución es agregar SomeFunc<int>() al encabezado también.

9

¿Ha agregado un prototipo con parámetros a su archivo de encabezado?

que quiero decir es que hay algún lugar en fileA.h

template<> SomeFunc<int>(); 

Si no que es probablemente la razón.

0

Brandon: eso es lo que pensé - nunca se debería llamar a la función especializada. Lo cual es cierto para la segunda aplicación que mencioné. La primera aplicación, sin embargo, claramente llama a la forma especializada a pesar de que la especialización no está declarada en el archivo de encabezado.

busco principalmente la iluminación aquí :-) porque la primera aplicación es una prueba de la unidad, y es lamentable tener un error que no aparece en la prueba, pero en la aplicación real de ...

(PS : He solucionado este error específico, de hecho al declarar la especialización en el encabezado, pero ¿qué otros errores similares todavía pueden estar ocultos?)

2

Según las especificaciones, su plantilla de función especializada nunca debe llamarse fuera de fileA.C, a menos que export la definición de la plantilla, que ningún compilador (excepto Comeau) admite actualmente (o tiene planeado para el futuro previsible).

Por otro lado, una vez que se crea una instancia de la plantilla de función, hay una función visible para el compilador que ya no es una plantilla. GCC puede volver a utilizar esta definición en diferentes unidades de compilación porque el estándar establece que cada plantilla solo se instanciará una vez para un conjunto determinado de argumentos de tipo [temp.spec]. Aún así, dado que la plantilla no se exporta, esto debe limitarse a la unidad de compilación.

Creo que GCC puede exponer un error aquí al compartir su lista de plantillas instanciadas en unidades de compilación. Normalmente, esta es una optimización razonable, pero debe tener en cuenta las especializaciones de función que no parece funcionar correctamente.

0

En Microsoft C++, hice un experimento con funciones en línea. Quería saber qué pasaría si definiera versiones incompatibles de una función en diferentes fuentes. Obtuve diferentes resultados dependiendo de si estaba usando una versión de Debug o una versión de Release. En Debug, el compilador se rehúsa a alinear cualquier cosa, y el enlazador estaba vinculando la misma versión de la función, sin importar el alcance de la fuente.En Release, el compilador ingresó la versión que se haya definido en ese momento, y usted tiene diferentes versiones de la función.

En ningún caso hubo advertencias. De alguna manera sospeché esto, y por eso hice el experimento.

Supongo que las funciones de la plantilla se comportarían igual que otros compiladores.

22

Es un error para tener una especialización para una plantilla que no está visible en el momento de la llamada. Desafortunadamente, los compiladores no están obligados a diagnosticar este error, y luego pueden hacer lo que quieran con su código (en la norma, está "mal formado, no se requiere diagnóstico").

Técnicamente, es necesario definir la especialización en el archivo de cabecera, pero casi todos compilador se encargará de esto como se podría esperar: este se fija en C++ 11 con la nueva instalación "plantilla extern":

extern template<> SomeFunc<int>(); 

Esto declara explícitamente que la especialización particular se define en otra parte. Muchos compiladores ya lo soportan, algunos con y algunos sin el extern.

0

@ [Anthony-Williams],

¿Seguro que no estás confundiendo extern declaraciones plantilla con extern template ejemplificaciones? Por lo que veo, extern template puede solo se utilizará para creación de instancias explícitas, no para especialización (lo que implica una instanciación implícita). [Temp.expl.spec] no se menciona la palabra clave extern:

explícita especialización:
        template <>declaración

+0

Damnit, la vinculación automática del nombre de usuario no funciona. –

3

que tenía el mismo problema con gcc4, así es como lo resolví. Era una solución más simple de lo que me llevaron a creer en comentarios anteriores. Las ideas de publicaciones anteriores eran correctas, pero su sintaxis no me funcionaba.


    ----------header----------------- 
    template < class A > 
    void foobar(A& object) 
    { 
     std::cout << object; 
    } 

    template <> 
    void foobar(int); 

    ---------source------------------ 
    #include "header.hpp" 

    template <> 
    void foobar(int x) 
    { 
     std::cout << "an int"; 
    } 

+0

SOB que fue formateado en la vista previa! lo siento –

1

Como dice Anthony Williams, el constructo extern template es la forma correcta de hacer esto, pero desde su código de ejemplo es incompleta y tiene varios errores de sintaxis, aquí es una solución completa.

fileA.h:

namespace myNamespace { 
    class classA { 
    public: 
     template <class T> void SomeFunc() { ... } 
    }; 

    // The following line declares the specialization SomeFunc<int>(). 
    template <> void classA::SomeFunc<int>(); 

    // The following line externalizes the instantiation of the previously 
    // declared specialization SomeFunc<int>(). If the preceding line is omitted, 
    // the following line PREVENTS the specialization of SomeFunc<int>(); 
    // SomeFunc<int>() will not be usable unless it is manually instantiated 
    // separately). When the preceding line is included, all the compilers I 
    // tested this on, including gcc, behave exactly the same (throwing a link 
    // error if the specialization of SomeFunc<int>() is not instantiated 
    // separately), regardless of whether or not the following line is included; 
    // however, my understanding is that nothing in the standard requires that 
    // behavior if the following line is NOT included. 
    extern template void classA::SomeFunc<int>(); 
} 

fileA.C:

#include "fileA.h" 

template <> void myNamespace::classA::SomeFunc<int>() { ... } 
Cuestiones relacionadas