2009-10-21 7 views
79

J'ai parfois vu des messages d'erreur vraiment indéchiffrables crachés par gcc lorsque j'utilisais des templates ... Plus précisément, j'ai eu des problèmes où des déclarations apparemment correctes provoquaient des erreurs de compilation très étranges qui s'envoyaient magiquement en préfixant le "typename" mot-clé au début de la déclaration ... (Par exemple, la semaine dernière, je déclarais deux itérateurs en tant que membres d'une autre classe modélisée et je devais le faire) ...Officiellement, à quoi sert le nom de fichier?

Quelle est l'histoire sur typename?

+3

http://stackoverflow.com/questions/1600464/ – sbi

Répondre

124

Après la citation du livre Josuttis :

le typename mot-clé a été présenté à préciser que l'identifiant qui suit est un type. Prenons l'exemple suivant :

template <class T> 
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 nom de type, le sous-type serait considéré comme un membre statique. Ainsi

T::SubType * ptr 

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

+1

Grand livre. Lisez-le une fois puis gardez-le comme référence si vous le souhaitez. –

+0

bonne réponse !!! – Gob00st

20

Stan Lippman's BLog post suggère: -

Stroustrup réutilisé la classe existante mot-clé pour spécifier un paramètre de type plutôt que d'introduire un nouveau mot-clé qui pourrait bien sûr casser programmes existants. Ce n'est pas qu'un nouveau mot-clé n'a pas été pris en compte - seulement que n'a pas été considéré comme étant donné sa perturbation potentielle . Et jusqu'à la norme ISO-C++ , c'était la seule manière de déclarer un paramètre de type.

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

Comme l'exemple donné

la grammaire de la langue
template <class T> 
class Demonstration { 
public: 
void method() { 
    T::A *aObj; // oops … 
    // … 
}; 

interprètent mal T::A *aObj; comme expression arithmétique donc un nouveau mot-clé est introduit appelé typename

typename T::A* a6; 

indique au compilateur de traiter l'instruction suivante comme une déclaration.

Depuis le mot-clé était sur la feuille de paie, Heck , pourquoi pas résolu la confusion causée par la décision initiale de réutiliser le mot-clé de classe .

C'est pourquoi nous avons tous les deux

Vous pouvez jeter un oeil à this post, il va certainement vous aider, je viens en extrait autant que je pouvais

+0

Oui, mais alors pourquoi était un nouveau mot-clé 'typename' nécessaire, si vous pouvez utiliser le mot-clé existant' class' dans le même but? – Jesper

+4

@Jesper: Je pense que la réponse de Xenus est confuse ici. 'typename' est devenu nécessaire pour résoudre le problème d'analyse syntaxique décrit dans la réponse de Naveen en citant Josuttis. (Je ne pense pas que l'insertion d'un 'class' à cet endroit aurait fonctionné.) Seulement après que le nouveau mot-clé a été accepté pour ce cas, il était également autorisé dans les déclarations d'argument de modèle (_ou est ces définitions? _), Parce que' classe »il a toujours été quelque peu trompeur. – sbi

11

Tenir compte du code

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

Malheureusement, le compilateur n'a pas besoin d'être psychique, et ne sait pas si T :: sometype finira par référence à un nom de type ou un membre statique T. Ainsi, on utilise typename pour lui dire:

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

Deux utilisations:

  1. En tant que mot-clé argument de modèle (au lieu de 'classe')
  2. Un mot clé typename indique au compilateur que un identifiant est un type (au lieu d'une variable membre statique)
template <typename T> class X // [1] 
{ 
    typename T::Y _member; // [2] 
} 
3

Le 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<typename T> 
struct test { 
    typedef T* ptr; 
}; 

template<>   // complete specialization 
struct test<int> { // 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<bool> le type reference semble complètement différent que pour les autres T s. Certes cela ne change pas le genre 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 test. Quelque chose comme ça

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

il semble être ok pour vous parce que vous attendez que test<T>::ptr est un type. Mais le compilateur ne sait pas et en fait il est même conseillé par la norme d'attendre le contraire, test<T>::ptr n'est pas un type. Pour dire au compilateur ce que vous attendez vous devez ajouter un typename avant. Le modèle correct ressemble à ce

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

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

5

Dans certaines situations où vous faites référence à un membre de type dépendant (signifiant "dépendant du paramètre de modèle"), le 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, d'un membre ou d'un autre). Dans de tels cas, vous devez désambiguïser la situation en disant 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 <class T> struct S { 
    typename T::type i; 
}; 

Dans cet exemple, le mot-clé dans typename nécessaire pour le code à compiler. La même chose se produit lorsque vous souhaitez faire référence à 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 mot-clé template, bien qu'il soit placé différemment

template <class T> struct S { 
    T::template ptr<int> p; 
}; 

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

template <class T> struct S { 
    typename T::template ptr<int>::type i; 
}; 

(si je suis la syntaxe correcte) .

Bien sûr, un autre rôle du mot clé typename doit être utilisé dans les déclarations de paramètres de modèle.

+0

Voir aussi [Une description du mot-clé typename C++] (http://pages.cs.wisc.edu/~driscoll/typename.html) pour plus d'informations (d'arrière-plan). – Atafar

1
#include <iostream> 

class A { 
public: 
    typedef int my_t; 
}; 

template <class T> 
class B { 
public: 
    // T::my_t *ptr; // It will produce compilation error 
    typename T::my_t *ptr; // It will output 5 
}; 

int main() { 
    B<A> b; 
    int my_int = 5; 
    b.ptr = &my_int; 
    std::cout << *b.ptr; 
    std::cin.ignore(); 
    return 0; 
} 
Questions connexes