web-dev-qa-db-fra.com

Quand le mot-clé "typename" est-il nécessaire?

Duplicata possible:
Officiellement, à quoi sert le nom de type?
Où et pourquoi dois-je mettre les mots-clés du modèle et du nom de frappe?

considérez le code ci-dessous:

template<class K>
class C {
    struct P {};
    vector<P> vec;
    void f();
};

template<class K> void C<K>::f() {
    typename vector<P>::iterator p = vec.begin();
}

Pourquoi le mot-clé "typename" est-il nécessaire dans cet exemple? Y a-t-il d'autres cas où "nom de type" doit être spécifié?

50
Martin

Réponse courte: chaque fois que vous faites référence à un nom imbriqué qui est un nom dépendant, c'est-à-dire imbriqué dans une instance de modèle avec un paramètre inconnu.

Réponse longue: Il existe trois niveaux d'entités en C++: les valeurs, les types et les modèles. Tous ceux-ci peuvent avoir des noms, et le nom seul ne vous dit pas de quel niveau d'entité il s'agit. Les informations sur la nature de l'entité d'un nom doivent plutôt être déduites du contexte.

Chaque fois que cette inférence est impossible, vous devez la spécifier:

template <typename> struct Magic; // defined somewhere else

template <typename T> struct A
{
  static const int value = Magic<T>::gnarl; // assumed "value"

  typedef typename Magic<T>::brugh my_type; // decreed "type"
  //      ^^^^^^^^

  void foo() {
    Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template"
    //        ^^^^^^^^
  }
};

Voici les noms Magic<T>::gnarl, Magic<T>::brugh et Magic<T>::kwpq a dû être explicité, car il est impossible de dire: Puisque Magic est un modèle, le très nature du type Magic<T> dépend de T - il peut y avoir des spécialisations qui sont entièrement différentes du modèle principal, par exemple.

Ce qui rend Magic<T>::gnarl un nom dépendant est le fait que nous sommes à l'intérieur d'une définition de modèle, où T est inconnu. Avions-nous utilisé Magic<int>, ce serait différent, puisque le compilateur connaît (vous le promettez!) la définition complète de Magic<int>.

(Si vous voulez tester cela vous-même, voici un exemple de définition de Magic que vous pouvez utiliser. Pardonnez l'utilisation de constexpr dans la spécialisation pour plus de brièveté; si vous avez un ancien compilateur, n'hésitez pas pour changer la déclaration de constante de membre statique au format pré-C++ 11 à l'ancienne.)

template <typename T> struct Magic
{
  static const T                    gnarl;
  typedef T &                       brugh;
  template <typename S> static void kwpq(int, char, double) { T x; }
};
template <> struct Magic<signed char>
{
  // note that `gnarl` is absent
  static constexpr long double brugh = 0.25;  // `brugh` is now a value
  template <typename S> static int kwpq(int a, int b) { return a + b; }
};

Usage:

int main()
{
  A<int> a;
  a.foo();

  return Magic<signed char>::kwpq<float>(2, 3);  // no disambiguation here!
}
70
Kerrek SB

Le mot clé typename est nécessaire car iterator est un type dépendant de P. Le compilateur ne peut pas deviner si iterator fait référence à une valeur ou à un type, il suppose donc que c'est une valeur à moins que vous ne criiez typename. Il est nécessaire chaque fois qu'un type dépend d'un argument de modèle, dans un contexte où les types ou les valeurs seraient valides. Par exemple, comme les classes de base typename ne sont pas nécessaires car une classe de base doit être un type.

Sur le même sujet, un mot clé template est utilisé pour indiquer au compilateur qu'un nom dépendant est une fonction de modèle au lieu d'une valeur.

11
K-ballo

Le mot-clé typename est nécessaire chaque fois qu'un nom de type dépend d'un paramètre de modèle (afin que le compilateur puisse `` connaître '' la sémantique d'un identifiant (( type ou valeur ) sans avoir une table de symboles complète au premier passage).


Pas dans le même sens, et un peu moins courant, le seul mot-clé de nom de type peut également être utile lors de l'utilisation de paramètres de modèle génériques: http: //ideone.com/amImX

#include <string>
#include <list>
#include <vector>

template <template <typename, typename> class Container,
          template <typename> class Alloc = std::allocator>
struct ContainerTests 
{
    typedef Container<int, Alloc<int> > IntContainer;
    typedef Container<std::string, Alloc<int> > StringContainer;
    //
    void DoTests()
    {
        IntContainer ints;
        StringContainer strings;
        // ... etc
    }
};

int main()
{
    ContainerTests<std::vector> t1;
    ContainerTests<std::list>   t2;

    t1.DoTests();
    t2.DoTests();
}
8
sehe