Officiellement, à quoi sert typename?

A l’occasion, j’ai vu des messages d’erreur vraiment indéchiffrables crachés par gcc lors de l’utilisation de templates … En particulier, j’ai eu des problèmes où des déclarations apparemment correctes causaient des erreurs de compilation très étranges qui disparaissaient comme par magie avec le mot-clé “typename” le début de la déclaration … (Par exemple, la semaine dernière, je déclarais deux iterators comme membres d’une autre classe de modèles et je devais le faire) …

Quelle est l’histoire sur typename?

Voici la citation du livre de Josuttis:

Le mot-clé nom_type a été introduit pour spécifier que l’identificateur qui suit est un type. Prenons l’exemple suivant:

template  Class MyClass { typename T::SubType * ptr; ... }; 

Ici, typename est utilisé pour préciser que SubType est un type de classe T. Ainsi, ptr est un pointeur sur le type T :: SubType. Sans typename, SubType serait considéré comme un membre statique. Ainsi

 T::SubType * ptr 

serait une multiplication de la valeur SubType de type T avec ptr.

Le post BLog de Stan Lippman suggère: –

Stroustrup a réutilisé le mot-clé de classe existant pour spécifier un paramètre de type plutôt que d’introduire un nouveau mot-clé qui pourrait bien sûr briser les programmes existants. Ce n’est pas qu’un nouveau mot-clé n’a pas été pris en compte, mais simplement qu’il n’a pas été jugé nécessaire compte tenu de ses perturbations potentielles. Et jusqu’à la norme ISO-C ++, c’était la seule façon de déclarer un paramètre de type.

Donc, fondamentalement, Stroustrup mot-clé de classe réutilisé sans introduire un nouveau mot-clé qui est modifié ultérieurement dans la norme pour les raisons suivantes

Comme l’exemple donné

 template  class Demonstration { public: void method() { T::A *aObj; // oops … // … }; 

la grammaire du langage interprète mal T::A *aObj; en tant qu’expression arithmétique, un nouveau mot-clé appelé typename

 typename T::A* a6; 

il demande au compilateur de traiter la déclaration suivante comme une déclaration.

Comme le mot-clé figurait sur la paie, pourquoi ne pas corriger la confusion provoquée par la décision initiale de réutiliser le mot-clé de classe.

Thats pourquoi nous avons les deux

Vous pouvez jeter un oeil à cet article , cela va certainement vous aider, je viens d’en extraire autant que possible

Considérez le code

 template somefunction( T * arg ) { T::sometype x; // broken . . 

Malheureusement, le compilateur n’est pas obligé d’être psychique et ne sait pas si T :: sometype finira par se référer à un nom de type ou à un membre statique de T. Donc, on utilise typename pour lui dire:

 template somefunction( T * arg ) { typename T::sometype x; // works! . . 

Dans certaines situations où vous faites référence à un membre du type dit dépendant (signifiant “dépendant du paramètre template”), le compilateur ne peut pas toujours déduire sans ambiguïté la signification sémantique de la construction résultante, car il ne sait pas quel type de nom (c’est-à-dire s’il s’agit d’un nom de type, d’un nom d’un membre de données ou d’un nom d’autre). Dans de tels cas, vous devez désambiguïser la situation en indiquant explicitement au compilateur que le nom appartient à un nom de type défini en tant que membre de ce type dépendant.

Par exemple

 template  struct S { typename T::type i; }; 

Dans cet exemple, le mot-clé typename nécessaire pour que le code comstack.

La même chose se produit lorsque vous souhaitez vous référer à un membre de modèle de type dépendant, c’est-à-dire à un nom qui désigne un modèle. Vous devez également aider le compilateur en utilisant le template mot clé, même s’il est placé différemment

 template  struct S { T::template ptr p; }; 

Dans certains cas, il peut être nécessaire d’utiliser les deux

 template  struct S { typename T::template ptr::type i; }; 

(si j’ai la syntaxe correctement).

Bien entendu, un autre rôle du mot-clé typename doit être utilisé dans les déclarations de parameters de modèle.

Deux utilisations:

  1. En tant que mot-clé d’argument de modèle (au lieu de ‘classe’)
  2. Un mot-clé typename indique au compilateur qu’un identificateur est un type (plutôt qu’une variable membre statique)
 template  class X // [1] { typename T::Y _member; // [2] } 

Le secret réside dans le fait qu’un modèle peut être spécialisé pour certains types. Cela signifie qu’il peut également définir l’interface complètement différente pour plusieurs types. Par exemple, vous pouvez écrire:

 template struct test { typedef T* ptr; }; template<> // complete specialization struct test { // for the case T is int T* ptr; }; 

On pourrait se demander pourquoi est-ce utile et en effet: cela semble vraiment inutile. Mais gardez à l’esprit que par exemple std::vector le type de reference est complètement différent de celui des autres T s. Certes, cela ne change pas le type de reference d’un type à quelque chose de différent mais néanmoins cela pourrait arriver.

Maintenant, que se passe-t-il si vous écrivez vos propres modèles en utilisant ce modèle de test . Quelque chose comme ça

 template void print(T& x) { test::ptr p = &x; std::cout << *p << std::endl; } 

ça semble aller pour vous parce que vous vous attendez à ce que le test::ptr soit un type. Mais le compilateur ne sait pas et en fait il est même conseillé par la norme d’attendre le contraire, le test::ptr n’est pas un type. Pour indiquer au compilateur ce que vous attendez, vous devez append un nom de typename avant. Le bon modèle ressemble à ceci

 template void print(T& x) { typename test::ptr p = &x; std::cout << *p << std::endl; } 

Bottom line: Vous devez append typename avant chaque fois que vous utilisez un type nested d'un modèle dans vos modèles. (Bien sûr, si un paramètre de modèle de votre modèle est utilisé pour ce modèle interne.)

 #include  class A { public: typedef int my_t; }; template  class B { public: // T::my_t *ptr; // It will produce compilation error typename T::my_t *ptr; // It will output 5 }; int main() { B b; int my_int = 5; b.ptr = &my_int; std::cout << *b.ptr; std::cin.ignore(); return 0; }