3 votes

Le compilateur choisit une mauvaise spécialisation de la fonction template

J'essaie désespérément de faire fonctionner mes spécialisations mais j'ai toujours du code non compilable à cause de la déduction d'arguments incorrects. Notez que les erreurs ne sont pas liées à la définition des modèles, mais à l'application d'une opération non pertinente dans une mise en œuvre incorrecte du modèle. L'exemple réduit de code démontrant le problème, est le suivant :

struct Test { void Method() const {} };

template<typename T>
void Cmp(T _val) { _val > 1; }

template<>
void Cmp<const Test &>(const Test &_val) { _val.Method(); }

template<>
void Cmp<const char *>(const char *_val) { _val[2]; }

int main()
{
  Test test1;
  char test2[5];

  Cmp(10);    // ok, expected
  Cmp(test1); // error in Cmp(T)?! but expecting to instantiate Cmp(const Test &)
  Cmp(test2); // error in Cmp(T)?! but expecting to instantiate Cmp(const char *)
  return 0;
}

Je ne souhaite vraiment pas utiliser des appels explicites comme Cmp<const Test &>(test1) (qui fonctionne) car, à ma connaissance, le compilateur devrait être capable de déduire les arguments automatiquement et l'idée derrière ces spécialisations est de répartir les appels Cmp de manière transparente (dans le code réel, je définis les opérateurs). Bien sûr, la spécialisation par valeur Cmp<Test>(Test) fonctionne comme prévu, mais pour les grandes classes sophistiquées non-POD, il est tout simplement ridicule de les passer par valeur.

Quelles que soient les corrections que j'essaie d'appliquer, le compilateur refuse obstinément de choisir la spécialisation par référence en utilisant le modèle général à la place. Il semble que j'ai raté quelque chose d'important, mais je suis vraiment à court d'idées pour expliquer pourquoi mon approche ne fonctionne pas et comment je devrais construire du code exprimant un concept aussi simple du monde C++ non modélisé que le passage de classes par référence. Bien sûr, Google s'est avéré être totalement inutile pour ce problème. J'ai essayé GCC 4.2.1 et 4.4.6.

13voto

James McNellis Points 193607
Cmp(test1);

Ici, T est déduit de l'argument pour être Test . On n'en déduit pas qu'il s'agit const Test& donc votre spécialisation ne correspond pas, d'où l'instanciation du modèle primaire. Vous devriez faire en sorte que votre spécialisation prenne une valeur de Test par valeur. Avec cette déclaration, la spécialisation est effectivement utilisée :

template<>
void Cmp<Test>(Test _val) { _val.Method(); }

Cmp(test1);

Ici, T est déduit de char* pas const char* donc la spécialisation ne correspond pas. Vous devriez vous spécialiser pour char* au lieu de const char* pour correspondre à cela. Alternativement, vous pouvez convertir l'argument en const char* lors de l'appel :

const char* test2ptr = test2;
Cmp(test2ptr); 

Tout cela dit, pourquoi se spécialiser quand on peut surcharger ?

template<typename T>
void Cmp(T _val) { _val > 1; }

void Cmp(const Test &_val) { _val.Method(); }

void Cmp(const char *_val) { _val[2]; }

// Add an overload to support arrays of char:
template <unsigned N>
void Cmp(const char (&_val)[N]) { _val[1]; }

Vous devriez vraiment éviter de spécialiser les modèles de fonctions. C'est difficile et, dans la plupart des cas, cela n'en vaut pas la peine.

4voto

Xeo Points 69818

Ehm... pourquoi se spécialiser ? Il suffit de le surcharger...

void Cmp(const Test &_val) { _val.Method(); }

void Cmp(const char *_val) { _val[2]; }

Garantie à 100% pour choisir ces méthodes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X