spécialisation explicite de la fonction membre de classe de modèle

88

J'ai besoin de spécialiser la fonction membre de modèle pour un type (disons double ). Cela fonctionne bien alors que la classe Xelle-même n'est pas une classe de modèle, mais quand je la crée, GCC commence à donner des erreurs de compilation.

#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();
}

Voici le message d'erreur

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()

Comment puis-je résoudre ce problème et quel est le problème ici?

Merci d'avance.

ledokol
la source
2
c'est illégal dans la norme actuelle, pour se spécialiser, il faut aussi spécialiser la classe ...
Nim
mais cela fonctionne si la classe n'est pas un modèle. Est-ce aussi illégal?
ledokol
non, c'est parfaitement bien, c'est uniquement pour les modèles de classes que cette règle s'applique (AFAIK).
Nim

Réponses:

108

Cela ne fonctionne pas de cette façon. Vous auriez besoin de dire ce qui suit, mais ce n'est pas correct

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

}

Les membres explicitement spécialisés ont besoin que leurs modèles de classe environnants soient également explicitement spécialisés. Vous devez donc dire ce qui suit, ce qui ne spécialiserait le membre que pour X<int>.

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

}

Si vous souhaitez que le modèle environnant ne soit pas spécialisé, vous avez plusieurs choix. Je préfère les surcharges

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>) {

   }
};
Johannes Schaub - litb
la source
pourquoi avez-vous besoin de l' type<>emballage? un cast d'un 0 vers un pointeur de type Tne pourrait-il pas faire l'affaire? Je suppose que ce n'est pas aussi élégant ...
Nim
Il semble que ce ne soit vraiment pas possible. Merci.
ledokol
3
@Nim à droite, je pense que le truc de fonte de pointeur est moche et ne fonctionnerait pas pour les types sur lesquels vous ne pouvez pas former de pointeurs (références). De plus, avoir un paramètre de fonction comme un pointeur vers un type de tableau sans taille est illégal en C ++. L'avoir dans un wrapper de type le fait fonctionner pour tous les types.
Johannes Schaub - litb
2
@ JohannesSchaub-litb: Quels sont les autres choix en cas de surcharge? Pouvez-vous en montrer quelques-uns?
Jean-Bernard Jansen
2
@ fast-reflexes son commentaire était d'utiliser template<typename T> void get_as(T*); void get_as(double*);et de passer un (T*)0.
Johannes Schaub - litb
24

Si on est capable de l'utiliser, std::enable_ifon pourrait compter sur SFINAE (l'échec de la substitution n'est pas une erreur)

cela fonctionnerait comme ça (voir EN DIRECT ):

#include <iostream>
#include <type_traits>

template <typename C> class X
{
public:
    template <typename T, 
              std::enable_if_t<!std::is_same_v<double,T>, int> = 0> 
    void get_as() { std::cout << "get as T" << std::endl; }

    template <typename T, 
              std::enable_if_t<std::is_same_v<double,T>, int> = 0> 
    void get_as() { std::cout << "get as double" << std::endl; }
};

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

   return 0;
}

La chose laide est que, avec tous ces enable_if, une seule spécialisation doit être disponible pour le compilateur, sinon une erreur d'homonymie se produira. C'est pourquoi le comportement par défaut "get as T" a également besoin d'un enable if.

Gabriel
la source
Mise à jour du message pour le rendre plus moderne.
Gabriel le