Différence des mots clés «nom de type» et «classe» dans les modèles?

504

Pour les modèles, j'ai vu les deux déclarations:

template < typename T >
template < class T >

Quelle est la différence?

Et que signifient exactement ces mots clés dans l'exemple suivant (extrait de l'article Wikipédia allemand sur les modèles)?

template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};
Tapis
la source

Réponses:

430

typenameet classsont interchangeables dans le cas de base de la spécification d'un modèle:

template<class T>
class Foo
{
};

et

template<typename T>
class Foo
{
};

sont équivalents.

Cela dit, il existe des cas spécifiques où il y a une différence entre typenameetclass .

Le premier concerne les types dépendants. typenameest utilisé pour déclarer lorsque vous faites référence à un type imbriqué qui dépend d'un autre paramètre de modèle, tel que typedefdans cet exemple:

template<typename param_t>
class Foo
{
    typedef typename param_t::baz sub_t;
};

La deuxième que vous montrez réellement dans votre question, même si vous ne vous en rendez peut-être pas compte:

template < template < typename, typename > class Container, typename Type >

Lors de la spécification d'un modèle de modèle , le classmot clé DOIT être utilisé comme ci-dessus - il n'est pas interchangeable avec typenamedans ce cas (remarque: depuis C ++ 17, les deux mots clés sont autorisés dans ce cas) .

Vous devez également utiliser classlors de l'instanciation explicite d'un modèle:

template class Foo<int>;

Je suis sûr qu'il y a d'autres cas que j'ai ratés, mais le résultat est: ces deux mots clés ne sont pas équivalents, et ce sont des cas courants où vous devez utiliser l'un ou l'autre.

Aaron Klotz
la source
45
Ce dernier est à peu près un cas particulier du fait que vous devez utiliser une classe ou une structure, pas un nom de type, pour définir une classe. De toute évidence, aucun de vos deux premiers bits de code n'a pu être remplacé template <typename T> typename Foo {};, car Foo <T> est certainement une classe.
Steve Jessop
2
std::vector<int>::value_typen'est pas un type dépendant, vous n'en avez pas besoin typename- vous n'en avez besoin que si un type dépend d'un paramètre de modèle, par exempletemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche
2
Et encore une fois, param_tn'est pas un type dépendant. Les types dépendants sont des noms qui dépendent d'un paramètre de modèle , par exemple foo<param_t>::some_type, pas des paramètres de modèle eux-mêmes.
Georg Fritzsche
2
Une proposition C ++ 1z N4051 vous permettra d'utiliser typename, c'est à dire template <typename> typename C.
user4112979
4
Depuis GCC 5, G ++ autorise désormais le nom de type dans un paramètre de modèle de modèle .
Chnossos
95

Pour nommer les paramètres du modèle, typenameetclass sont équivalents. §14.1.2:

Il n'y a pas de différence sémantique entre la classe et le nom de type dans un paramètre de modèle.

typenamecependant, il est possible dans un autre contexte lorsque vous utilisez des modèles - d'indiquer au compilateur que vous faites référence à un type dépendant. §14.6.2:

Un nom utilisé dans une déclaration ou une définition de modèle et qui dépend d'un paramètre de modèle est supposé ne pas nommer un type, sauf si la recherche de nom applicable trouve un nom de type ou que le nom est qualifié par le mot-clé nom-type.

Exemple:

typename some_template<T>::some_type

Sans typenamele compilateur, vous ne pouvez pas dire en général si vous faites référence à un type ou non.

Georg Fritzsche
la source
2
Je comprends la règle, mais qu'est-ce qui empêche exactement le compilateur de traiter some_template <T> comme un type en interne? Désolé si je manque quelque chose d'évident.
batbrat
23

Bien qu'il n'y ait pas de différence technique, j'ai vu que les deux désignaient des choses légèrement différentes.

Pour un modèle qui devrait accepter tout type comme T, y compris les éléments intégrés (tels qu'un tableau)

template<typename T>
class Foo { ... }

Pour un modèle qui ne fonctionnera que lorsque T est une vraie classe.

template<class T>
class Foo { ... }

Mais gardez à l'esprit que certaines personnes utilisent uniquement le style. Non mandaté par la norme ou imposé par les compilateurs

Michael Anderson
la source
15
Je ne vous blâme pas de l'avoir mentionné, mais je pense que cette politique est assez erronée, car les programmeurs finissent par prendre du temps à penser à quelque chose qui n'a pas d'importance ("ai-je utilisé la bonne?") Pour indiquer quelque chose qui ne fonctionne pas '' t question ("existe-t-il un type intégré qui implémente l'interface requise pour ce paramètre de modèle?"). Si des membres du paramètre template sont utilisés ( T t; int i = t.toInt();), alors vous avez besoin d'une "vraie classe", et votre code ne se compilera pas si vous fournissez intpour T...
Steve Jessop
1
Si vous souhaitez limiter l'utilisation aux classes réelles, il est préférable d'ajouter une spécialisation pour lancer / provoquer une erreur pour les types non-classe. Si vous souhaitez limiter l'utilisation à des classes particulières, ne vous spécialisez que pour elles. En tout cas, une telle distinction stylistique est trop subtile pour faire passer le message.
Potatoswatter
2
Puisqu'ils signifiaient la même chose, veuillez n'en utiliser qu'une seule. Sinon, c'est comme utiliser inline {, sauf si c'est un mardi, puis vous utilisez la ligne suivante {.
Paul Draper du
+1 Je le fais moi-même parfois ... classimplique que vous n'attendez pas seulement une "valeur" prenant peut-être en charge certains opérateurs, copiez ou déplacez la construction et / ou l'affectation, mais avez spécifiquement besoin d'un type prenant en charge certaines sémantiques d'accès aux membres. Le coup d'œil le plus rapide sur la déclaration définit ensuite les attentes et décourage, par exemple, la fourniture de types intégrés pour les classparamètres alors que ce serait certainement une erreur.
Tony Delroy
Je voudrais comprendre quels types de situations réelles existent où un modèle fonctionnerait pour N'IMPORTE QUELLE classe, mais ne fonctionnerait pas avec les types intégrés. Avez-vous un exemple?
lfalin
7
  1. Aucune différence
  2. Le paramètre de type de modèle Containerest lui-même un modèle avec deux paramètres de type.
Nikolai Fetissov
la source
3
il y a une différence en général.
Hassan Syed
ces deux paramètres avec lesquels le conteneur est basé sur des modèles pourraient-ils également être nommés? dans l'exemple, ils n'ont pas de nom. Et aussi - dans cet exemple, il est écrit «class Container» - pourrait-il également être écrit «typename Container» à la place?
Mat
2
@Mat: oui, le terme à rechercher est paramètres / arguments de modèle de modèle . Par exemple:template<template<class U> class V> struct C {};
Georg Fritzsche
6

Cet extrait est extrait du livre de base c ++. Même si je suis sûr que c'est faux.

Chaque paramètre de type doit être précédé de la classe de mots-clés ou du nom de type:

// error: must precede U with either typename or class
template <typename T, U> T calc(const T&, const U&);

Ces mots clés ont la même signification et peuvent être utilisés de manière interchangeable dans une liste de paramètres de modèle. Une liste de paramètres de modèle peut utiliser les deux mots clés:

// ok: no distinction between typename and class in a template parameter list
template <typename T, class U> calc (const T&, const U&);

Il peut sembler plus intuitif d'utiliser le mot-clé typename plutôt que class pour désigner un paramètre de type de modèle. Après tout, nous pouvons utiliser des types intégrés (non-classe) comme argument de type de modèle. De plus, le nom de type indique plus clairement que le nom qui suit est un nom de type. Cependant, le nom de type a été ajouté à C ++ après que les modèles étaient déjà largement utilisés; certains programmeurs continuent d'utiliser exclusivement la classe

KK
la source