web-dev-qa-db-fra.com

Propagation de 'typedef' de la classe basée à la classe dérivée pour 'template'

J'essaie de définir la classe de base, qui contient uniquement typedef. 

template<typename T>
class A
{
public:
    typedef std::vector<T> Vec_t;
};


template<typename T>
class B : public A<T>
{
private:
    Vec_t v;  // fails - Vec_t is not recognized
};

Pourquoi en B je reçois un message d'erreur indiquant que Vec_t n'est pas reconnu et que je dois l'écrire explicitement?

typename A<T>::Vec_t v;
54
dimba

Je crois que cette question est en double, mais je ne le trouve pas maintenant. C++ Standard dit que vous devez qualifier complètement le nom selon 14.6.2/3:

Dans la définition d'un modèle de classe ou d'un membre d'un modèle de classe, si une classe de base du modèle de classe dépend d'un paramètre de modèle, la portée de la classe base n'est pas examinée lors de la recherche de nom non qualifiée, ni au point définition du modèle de classe ou du membre ou lors d'une instanciation du modèle de classe ou du membre.

UPD: J'ai enfin trouvé un doublon: le voilà .

42

Il existe quelque chose appelé dépendants et nondependent noms dans le cas de modèles.

Si le nom dépend du paramètre de modèle T, son nom dependant et les autres paramètres ne dépendent pas du paramètre T sont des noms independent.

Voici la règle: le compilateur ne le fait pas regardez dans les classes de base dépendantes (telles que A) lorsque vous recherchez une personne non dépendante noms (comme Vec_t). Par conséquent, le compilateur ne sait même pas qu'ils exister encore moins sont des types.

Le compilateur ne peut pas supposer que Vec_t est un type jusqu'à ce qu'il sache T car il existe une spécialisation potentielle de A<T>A<T>:: Vec_t est un membre de données.

Donc, la solution est d'utiliser typename 

 typename A<T>::Vec_t v;  ← good

Je vous recommande de passer par ceci https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-types .

Ancien lien (brisé): http://www.parashift.com/c++-faq-lite/templates.html#faq-35.18

31
Xinus

Parce que le compilateur n'est pas certain que Vec_t nomme un type. Par exemple, A<T> peut être spécialisé pour T=int à pas avoir cette typedef particulière.

7
Jesse Beder

Vous devez explicitement qualifier l'utilisation de Vec_t car le compilateur ne sait pas d'où provient Vec_t.

Il ne peut rien supposer sur la structure de A, car le modèle de classe A peut être spécialisé. La spécialisation peut inclure un Vec_t qui n'est pas une typedef ou même un membre Vec_t.

2
user200783

Pour être complet, voici comment vous pouvez atténuer un peu cette nuisance:

  • re-typedef ces types dans des classes dérivées, ou mieux - comme avec les méthodes -
  • il suffit d'importer ces noms dans la portée de la classe dérivée avec un using declaration:

template<typename T>
class A
{
public:
    typedef std::vector<T> Vec_t;
};


template<typename T>
class B : public A<T>
{
public:
    using typename A<T>::Vec_t;
    // .........

private:
    Vec_t v;
};

Cela peut être utile si vous avez plus d'une mention de la typedef héritée dans la classe dérivée. De plus, vous n'avez pas besoin d'ajouter typename à chaque fois.

1
Roman Kruglov

Vec_t n'est pas un nom dépendant, et le compilateur a besoin de savoir de quoi il s'agit sans instancier de modèle (classe de base dans ce cas). Ce n'est vraiment pas différent de:

template <class T>
class X
{
    std::string s;
}

Ici aussi, le compilateur doit connaître std :: string même si X n'est pas instancié, puisque le nom ne dépend pas de l'argument de modèle T (pour autant que le compilateur puisse en assumer).

Globalement, les typedefs dans une classe de base de modèle semblent plutôt inutiles pour une utilisation dans une classe dérivée. Les typedefs sont utiles pour l'utilisateur, cependant. 

1
UncleBens

Ce concept peut être associé à la façon dont nous utilisons std::vector<T>. Par exemple, si nous avons un std::vector<int> Foo. Maintenant, nous décidons d'utiliser n'importe lequel de ses types de membres, disons une iterator. Dans ce scénario, nous mentionnons explicitement

std::vector<int>::iterator foo_iterator;

De même, dans votre cas, pour utiliser un membre public de type Vec_t sur template <typename T> class A, vous devez le déclarer explicitement comme

A<T>::Vec_t v;
OR
A<int>::Vec_t int_type;
1
RAD