Question

Cela faisait longtemps que GCC ne m'avait pas attrapé avec celui-ci, mais c'est arrivé aujourd'hui. Mais je n’ai jamais compris pourquoi GCC exige que typedef soit nom_type dans les modèles, alors que VS et moi-même ICC ne le font pas. Le nom de type typedef est-il un & Quot; bogue & Quot; ou une norme trop stricte, ou quelque chose qui reste aux auteurs du compilateur?

Pour ceux qui ne savent pas ce que je veux dire, voici un exemple:

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    std::map<KEY,VALUE>::const_iterator iter = container.find(key);
    return iter!=container.end();
}

Le code ci-dessus est compilé dans VS (et probablement dans ICC), mais échoue dans GCC car il le veut comme ceci:

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    typedef typename std::map<KEY,VALUE>::const_iterator iterator; //typedef typename
    iterator iter = container.find(key);
    return iter!=container.end();
}

Remarque: ce n’est pas une fonction que j’utilise, mais juste une bêtise qui montre le problème.

Était-ce utile?

La solution

Le nom de type est requis par la norme. La compilation de modèles nécessite une vérification en deux étapes. Lors du premier passage, le compilateur doit vérifier la syntaxe du modèle sans fournir les substitutions de types. Dans cette étape, std :: map :: iterator est supposé être une valeur. S'il ne s'agit pas d'un type, le mot-clé typename est obligatoire.

Pourquoi est-ce nécessaire? Avant de substituer les types réels KEY et VALUE, le compilateur ne peut pas garantir que le modèle ne soit pas spécialisé et que la spécialisation ne redéfinisse pas le mot clé itérateur .

Vous pouvez le vérifier avec ce code:

class X {};
template <typename T>
struct Test
{
   typedef T value;
};
template <>
struct Test<X>
{
   static int value;
};
int Test<X>::value = 0;
template <typename T>
void f( T const & )
{
   Test<T>::value; // during first pass, Test<T>::value is interpreted as a value
}
int main()
{
  f( 5 );  // compilation error
  X x; f( x ); // compiles fine f: Test<T>::value is an integer
}

Le dernier appel a échoué avec une erreur indiquant que lors de la première étape de compilation du modèle de f (), Test :: value a été interprété comme une valeur, mais comme une instanciation du test < > un modèle de type X donne un type.

Autres conseils

Eh bien, GCC n’exige pas réellement que le typedef - typename soit suffisant. Cela fonctionne:

#include <iostream>
#include <map>

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    typename std::map<KEY,VALUE>::const_iterator iter = container.find(key);
    return iter!=container.end();
}

int main() {
    std::map<int, int> m;
    m[5] = 10;
    std::cout << find(m, 5) << std::endl;
    std::cout << find(m, 6) << std::endl;
    return 0;
}

Ceci est un exemple de problème d'analyse contextuelle. Ce que la ligne en question signifie ne ressort pas de la syntaxe de cette fonction uniquement. Vous devez savoir si std::map<KEY,VALUE>::const_iterator est un type ou non.

Maintenant, je n'arrive pas à penser à un exemple de ce que ... ::const_iterator pourrait être un type, ce ne serait pas non plus une erreur. Donc, je suppose que le compilateur peut découvrir qu’il doit être un type, mais cela pourrait être difficile pour le pauvre compilateur (rédacteurs).

La norme nécessite l’utilisation de <=> ici, conformément à la section 14.6 / 3 de la norme.

Il semble que VS / ICC fournisse le typename mot-clé chaque fois qu'il pense qu'il est requis. Notez qu’il s’agit d’une mauvaise idée (TM) de laisser le compilateur décider de ce que vous voulez . Cela complique encore le problème en inculquant la mauvaise habitude de sauter <=> lorsque cela est nécessaire et constitue un cauchemar pour la portabilité. Ce n'est certainement pas le comportement standard. Essayez en mode standard strict ou Comeau.

C’est un bogue du compilateur Microsoft C ++ - dans votre exemple, std :: map :: iterator peut ne pas être un type (vous pourriez avoir spécialisé std :: map sur KEY, VALUE afin que std :: map :: itérateur était une variable par exemple).

GCC vous oblige à écrire le code correct (même si ce que vous vouliez dire était évident), alors que le compilateur Microsoft devine correctement ce que vous vouliez dire (même si le code que vous avez écrit était incorrect).

Il convient de noter que le problème du type valeur / type n’est pas le problème fondamental. Le problème principal est l'analyse . Considérer

template<class T>
void f() { (T::x)(1); }

Il n'y a aucun moyen de savoir s'il s'agit d'un cast ou d'un appel de fonction, à moins que le mot-clé typename soit obligatoire. Dans ce cas, le code ci-dessus contient un appel de fonction. En général, le choix ne peut être différé sans renoncer complètement à l'analyse, considérons simplement fragment

(a)(b)(c)

Au cas où vous ne vous en souveniez pas, la distribution a une priorité supérieure à celle de l'appel de fonction en C, ce qui explique en partie pourquoi Bjarne souhaitait un style de fonction. Il n’est donc pas possible de dire si ce qui précède signifie

(a)(b)  (c)   // a is a typename

ou

(a) (b)(c)    // a is not a typename , b is

ou

(a)(b) (c)    // neither a nor b is a typename

où j'ai inséré un espace pour indiquer le regroupement.

Notez également " nom-modèle " mot-clé est obligatoire pour la même raison que & "; typename &"; vous ne pouvez pas analyser les éléments sans connaître leur nature dans C / C ++.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top