2011-07-15 18 views
7

Tengo un problema con la especialización de plantillas que me gustaría entender. Estoy trabajando con Visual C++ 10.0 (2010). Tengo una clase como esta:La especialización de plantillas falla al vincular

class VariableManager 
{ 
public: 

    template<typename VarT> 
     VarT get(std::string const& name) const 
     { 
        // Some code... 
     } 

    // This method supposed to be fully evaluated, linkable method. 
    template<> 
     std::string get<std::string>(std::string const& name) const; 

private: 
    std::map<std::string, boost::any> mVariables; 
}; 

En teoría, porque me especialicé el método "get", el enlazador debe ser capaz de recoger a partir de un archivo objeto. En su lugar, me sale un error de referencia sin resolver con el enlazador si coloco el método en el archivo de origen:

template<> 
    std::string VariableManager::get<std::string>(std::string const& name) const 
      { 
       // Doing something... 
      } 

Si coloco este método en el archivo de cabecera como en línea, la construcción va muy bien. Entiendo que las funciones de plantilla como esta:

 template<typename VarT> 
      VarT get(std::string const& name) const; 

debe ser colocado en la cabecera porque el compilador no será capaz de especializarse la plantilla de acuerdo con el código de llamada, pero en el caso de la plena especialización es la la implementación de la clase que hace que así el método de plantilla especializado ya debería existir como un símbolo público. ¿Podría alguien arrojar algo de luz sobre este tema?

Respuesta

10

Su análisis es correcto - una plantilla de función especializada explícitamente que tiene algún parámetro de plantilla especificado con valores explícitos proporciona una definición completa de una función.

Si ha incluido correctamente el archivo correspondiente .cpp que contiene la definición de especialización explícita en su proyecto, entonces VC++ no debería generar un error de enlazador. Sin embargo, para el cumplimiento de los estándares, permítanme señalar que debe declarar su especialización fuera de de la clase adjunta. El Estándar prohíbe declarar especializaciones explícitas dentro de la clase adjunta (y otros compiladores rechazarán su código). Así que cambiar el archivo de cabecera para declarar la especialización de este tipo, en lugar

class VariableManager 
{ 
public: 

    template<typename VarT> 
     VarT get(std::string const& name) const 
     { 
        // Some code... 
     } 

private: 
    std::map<std::string, boost::any> mVariables; 
}; 

// This method supposed to be fully evaluated, linkable method. 
template<> 
std::string VariableManager::get<std::string>(std::string const& name) const; 

También quiero señalar que no se puede llamar get<std::string> interior de su cuerpo de la clase. Esto se debe a que una llamada de ese tipo aún no vería la declaración explícita de especialización y, por lo tanto, trataría de crear una instancia de la función a partir de la definición de la plantilla. El estándar representa dicho código mal formado, sin que se requiera un diagnóstico.

+0

Es curioso, pero parece que el problema estándar de cumplimiento fue el problema. Eliminé la declaración de la clase e hice la declaración de especialización como sugirió, y el enlazador no tuvo problemas. Es bueno recordar que las especializaciones deben declararse fuera del alcance de la clase. ¡Gracias por su amable ayuda! – progician

+0

Es una verdadera suerte para mí encontrar tu respuesta. Todavía es un misterio para mí lo que hace el compilador si dejo la especialización de plantilla dentro de una clase y lo que hace que el enlazador no pueda encontrarla. Sin embargo, definitivamente salvaste mi día! – Nipheris

0

La especialización de una plantilla no obliga al compilador a crear una instancia (creo que GCC sí); aún necesita decirle explícitamente al compilador que cree una instancia de la plantilla. Puede hacerlo con instanciación de plantilla explícita. Básicamente, sólo tiene que añadir esto en el archivo de origen:

template std::string VariableManager::get(const std::string& name) const; 
+0

eso está mal. lo especializó así que no necesita ni quiere crear una instancia de la plantilla. –

+1

. litb tiene razón, esto de hecho está mal, pero dejaré mi respuesta aquí para que otros puedan ver que también está mal –

0

Método comenzando con template<> todavía se considera un métodotemplateespecialización. Entonces debe tener que poner en un archivo de encabezado.

Si quiere ponerlo en un archivo de implementación, entonces tiene que sobrecargar.

class VariableManager 
{ 
//... 
    VarT get(std::string const& name) const 
    {} 

    std::string get(std::string const& name) const; //overloading not specialization 
}; 
+0

No, no es así. Ver la respuesta de Reko. Y no estoy seguro de lo que intentas decir con "método de especialización de plantilla". –

+0

(** PERO ** Recomiendo sobrecargar la especialización _¡nuño_!) –

+0

@Tomalak, el OP intenta especializar el método 'template'' get' en la pregunta.Mi respuesta es que los métodos especializados deben declararse en los archivos de encabezado. ¿Puedes especificar más, qué está mal? – iammilind

Cuestiones relacionadas