2011-04-01 11 views
69

Necesito especializar la función de miembro de plantilla para algún tipo (digamos double). Funciona bien, mientras que la clase X no es una clase de plantilla, pero cuando la hago, GCC comienza a dar errores en tiempo de compilación.especialización explícita de la función de miembro de clase de plantilla

#include <iostream> 
#include <cmath> 

template <class C> class X 
{ 
public: 
    template <class T> void get_as(); 
}; 

template <class C> 
void X<C>::get_as<double>() 
{ 

} 

int main() 
{ 
    X<int> x; 
    x.get_as(); 
} 

aquí es el mensaje de error

source.cpp:11:27: error: template-id 
    'get_as<double>' in declaration of primary template 
source.cpp:11:6: error: prototype for 
    'void X<C>::get_as()' does not match any in class 'X<C>' 
source.cpp:7:35: error: candidate is: 
    template<class C> template<class T> void X::get_as() 

¿Cómo puedo solucionar eso y lo que es el problema aquí?

Gracias de antemano.

+2

esto es ilegal en el estándar actual, para especializarse, también debe especializar la clase ... – Nim

+0

pero funciona si la clase no es plantilla. ¿Es ilegal también? – ledokol

+0

no, eso está perfectamente bien, es solo para las plantillas de clase que esta regla aplica (AFAIK). – Nim

Respuesta

85

No funciona de esa manera. Usted tendría que decir lo siguiente, pero es no correcta

template <class C> template<> 
void X<C>::get_as<double>() 
{ 

} 

explícitamente specialized members necesitan que sus plantillas de clase circundante para ser especializados, así explícitamente. Por lo tanto, debe indicar lo siguiente, que solo especializaría al miembro para el X<int>.

template <> template<> 
void X<int>::get_as<double>() 
{ 

} 

Si desea mantener la plantilla circundante no especializada, tiene varias opciones. Yo prefiero sobrecargas

template <class C> class X 
{ 
    template<typename T> struct type { }; 

public: 
    template <class T> void get_as() { 
    get_as(type<T>()); 
    } 

private: 
    template<typename T> void get_as(type<T>) { 

    } 

    void get_as(type<double>) { 

    } 
}; 
+0

¿por qué necesita el contenedor 'type <>'? ¿No podría el reparto de un 0 a un puntero del tipo 'T' hacer el truco? Supongo que no es tan elegante ... – Nim

+0

Parece que esto realmente no es posible de hacer. Gracias. – ledokol

+3

@Nim a la derecha, creo que el puntero es feo, y no funcionaría para los tipos que no se pueden formar punteros (referencias). Además, tener un parámetro de función como un puntero a un tipo de matriz sin un tamaño es ilegal en C++. Tenerlo en una envoltura de tipo lo hace funcionar para todos los tipos. –

13

Si uno es capaz utilizado std::enable_if que podían confiar en SFINAE (insuficiencia sustitución no es un error)

que funcionaría así:

#include <iostream> 
#include <type_traits> 

template <class C> class X 
{ 
public: 
    template <class T, typename std::enable_if< ! std::is_same<double,T>::value>::type * = nullptr > void get_as(){ 
     std::cout << "get as T" << std::endl; 
    } 


    template <class T, typename std::enable_if< std::is_same<double,T>::value>::type * = nullptr > void get_as(){ 
     std::cout << "get as double" << std::endl; 
    } 
}; 


int main() 
{ 

    X<int> d; 
    d.get_as<double>(); 

    return 0; 
} 

Lo feo es que, con todas estas enable_if, solo una especialización necesita estar disponible para el compilador, de lo contrario surgirá un error de desambiguación. Es por eso que el comportamiento predeterminado "obtener como T" también necesita habilitar.

Cuestiones relacionadas