2009-06-01 5 views
8

J'ai une classe de visiteur ressemblant à ceci:Est-ce que les spécialisations de template nécessitent une syntaxe template <>?

struct Visitor 
{ 
    template <typename T> 
    void operator()(T t) 
    { 
     ... 
    } 

    void operator()(bool b) 
    { 
     ... 
    } 
}; 

Il est clair que, operator()(bool b) est destiné à être une spécialisation de la fonction précédente de modèle.

Cependant, il n'a pas la syntaxe template<> que j'ai l'habitude de voir avant, en déclarant ceci comme une spécialisation de modèle. Mais ça compile.

Est-ce sécuritaire? Est-ce correct?

Répondre

19

Votre code n'est pas une spécialisation de modèle, mais plutôt une fonction non basée sur un modèle. Il y a quelques différences là-bas. L'opérateur non-basé sur un modèle() aura la priorité sur une version modélisée (pour une correspondance exacte, mais les conversions de type ne se produira pas là), mais vous pouvez toujours forcer la fonction templated à appeler:

class Visitor 
{ 
public: // corrected as pointed by stefanB, thanks 
    template <typename T> 
    void operator()(T data) { 
     std::cout << "generic template" << std::endl; 
    } 
    void operator()(bool data) { 
     std::cout << "regular member function" << std::endl; 
    } 
}; 
template <> // Corrected: specialization is a new definition, not a declaration, thanks again stefanB 
void Visitor::operator()(int data) { 
    std::cout << "specialization" << std::endl; 
} 
int main() 
{ 
    Visitor v; 
    v(5); // specialization 
    v(true); // regular member function 
    v.operator()<bool>(true); // generic template even if there is a non-templated overload 
    // operator() must be specified there (signature of the method) for the compiler to 
    // detect what part is a template. You cannot use <> right after a variable name 
} 

Dans votre le code il n'y a pas beaucoup de différence, mais si votre code doit passer le type de paramètre de modèle, il obtiendra plus drôle:

template <typename T> 
T g() { 
    return T(); 
} 
template <> 
int g() { 
    return 0; 
} 
int g() { 
    return 1; 
} 
int main() 
{ 
    g<double>(); // return 0.0 
    g<int>(); // return 0 
    g(); // return 1 -- non-templated functions take precedence over templated ones 
} 
+0

Je crois que "template <> void operator() (int données) {" dans la section du code supérieur devrait être "template <> opérateur vide() (int data) {", et dans la section du bas, "int g() {"devrait être" int g () {"dans le bas (désolé, je ne sais pas comment styliser les sections de code dans les commentaires) –

+0

J'ai eu un doute, mais les compilateurs GCC et Comeau prennent le code comme valide. Je ne peux pas tester MSVS maintenant, si vous pouvez l'essayer, je l'apprécierais :) –

+0

encore jeter un oeil à la réponse ici http://stackoverflow.com/questions/937744/function-template-specialization-format – stefanB

4

Oh, ça va se compiler. Ce ne sera simplement pas une fonction de modèle. Vous aurez une fonction non-template régulière au lieu d'une spécialisation de template.

C'est sûr, et probablement ce que vous voulez aussi. Le modèle Visitor est normalement implémenté par surcharge. Modèles de fonctions de spécialisation isn't really a good idea anyway.

+0

Y at-il des situations où il se comporterait différemment? Est-ce un bug potentiel, ou juste un mauvais style? –

+0

Habituellement, c'est la bonne chose à faire. Vous devriez généralement préférer les surcharges simples aux spécialisations de modèle de fonction. Le comportement est plus prévisible. – jalf

+0

+1 pour le lien, juste ce que je cherchais. – avakar

2

Ce que vous avez fait n'est pas une sérialisation de modèle, mais une surcharge de fonction. C'est sûr.

P.S. Il est difficile de dire si c'est correct ou non, sans savoir ce que vous essayez d'accomplir. Gardez à l'esprit que peu importe le modèle ou la fonction surchargée, votre opérateur sera choisi à la compilation. Si vous avez besoin de dépêcher, vous avez besoin de polymorphisme, pas de surcharge. Eh bien, vous le savez probablement de toute façon; Au cas où.

5

Vous avez ici une surcharge de fonctions; pour obtenir la spécialisation de template, vous avez en effet besoin de la syntaxe template <>. Cependant, vous devez être conscient que ces deux approches, même si elles peuvent sembler identiques, sont subtilement différentes, et même le compilateur pourrait se perdre lors du choix de la bonne fonction à appeler. La liste de tous les cas possibles serait un peu trop longue pour cette réponse, mais vous voudrez peut-être vérifier Herb Sutter GoTW #49 sur le sujet.

2

Vous avez

  • void operator()(bool b) qui est non fonction basé sur un modèle
  • template< typename T > void operator()(T t) qui est un modèle de base séparée surchargeant le ci-dessus

Vous pourriez avoir une spécialisation complète de la deuxième comme dans template<> void operator(int i) qui ne serait pris en compte lors void operator()(bool b) ne correspondent pas.

La spécialisation du modèle de base est utilisée pour sélectionner les méthodes de modèle de base à appeler.Cependant, dans votre cas, vous avez une méthode non-basée sur un modèle qui sera considérée en premier.

L'article Why Not Specialize Function Templates? donne une assez bonne explication de la façon dont la méthode est sélectionnée.

En Sumary:

  1. fonctions non modèles sont considérés comme premier (c'est votre opérateur ordinaire() (bool) ci-dessus)
  2. modèles de base de la fonction se vérifier secondes (ceci est votre templated function), le template de base le plus spécialisé est sélectionné, puis s'il a une spécialisation pour les types exacts, sinon le template de base est utilisé avec les types 'correctes' (voir explication dans l'article)

Exemple:

#include <iostream> 
using namespace std; 

struct doh 
{ 
    void operator()(bool b) 
    { 
     cout << "operator()(bool b)" << endl; 
    } 

    template< typename T > void operator()(T t) 
    { 
     cout << "template <typename T> void operator()(T t)" << endl; 
    } 
}; 
// note can't specialize inline, have to declare outside of the class body 
template<> void doh::operator()<>(int i) 
{ 
    cout << "template <> void operator()<>(int i)" << endl; 
} 
template<> void doh::operator()<>(bool b) 
{ 
    cout << "template <> void operator()<>(bool b)" << endl; 
} 

int main() 
{ 
    doh d; 
    int i; 
    bool b; 
    d(b); 
    d(i); 
} 

Vous recevez des appels à:

operator()(bool b)  <-- first non template method that matches 
template <> void operator()(int i)  <-- the most specialized specialization of templated function is called 
+2

Les parenthèses d'argument manquent: template <> void doh :: opérateur() <> (bool b) (notez le "<>" i inséré), et notez qu'il (sutter) écrit explicitement que le template de base le plus spécialisé est sélectionné. Pas "celui avec la spécialisation la plus correspondante" ni "la spécialisation la plus spécialisée de la fonction modèle". La résolution considère explicitement uniquement les modèles primaires (de base). C'est ce qu'on appelle "l'ordonnancement partiel des modèles de fonction" et est décrit en 14.6.6.2 dans la norme. –

+0

Vous avez raison sur la sélection des templates de base, je suppose que j'ai court-circuité mon explication (typage paresseux?), J'essayais de dire que le template de base le plus spécialisé est selectionné et s'il a une spécialisation pour les types spécialisation est utilisée sinon le modèle de base est utilisé avec les types «corrects» – stefanB

+0

Sur gcc3.3.3 les deux avec des parenthèses template <> void doh :: opérateur() <> (bool b) et sans formulaire template <> void doh :: operator() (bool b) compile et m'a donné le même résultat, toujours copier/coller de l'exemple de @dribeas donnera des erreurs error: spécialisation explicite dans une portée sans espace de noms 'struct doh 'et ainsi de suite - c'est ce à quoi je faisais allusion (et parce que les méthodes sont toutes privées mais c'est sur note) – stefanB