31 votes

Un autre bug dans g ++ / Clang? [Les modèles C ++ sont amusants]

Découvrez le code suivant (écrit juste pour le plaisir)

 namespace N
{
   template<typename T>
   struct K
   {

   };
}
template<typename T>
struct X
{
   typename T::template K<T> *p; //should give error 
                                 //N::K<int> has no template member named `K`
};

int main()
{
   X<N::K<int> > l;
}
 

Le code est compilé sur g ++ (4.5.1) et Clang alors que Comeau et Intel C ++ donnent des erreurs (similaires).

Les erreurs que je commets sur Comeau sont les suivantes:

 "ComeauTest.c", line 13: error: class "N::K<int>" has no member "K"
     typename T::template K<T> *p;
                          ^
          detected during instantiation of class "X<T> [with T=N::K<int>]" at
                    line 18

"ComeauTest.c", line 13: error: expected an identifier
     typename T::template K<T> *p;
                           ^
          detected during instantiation of class "X<T> [with T=N::K<int>]" at
                    line 18
 

Ma question est donc la suivante: "L’échantillon de code est-il mal formé?" Selon moi "oui". Est-ce que cela signifie que c'est encore un autre bogue dans g ++ / Clang?

45voto

Johannes Schaub - litb Points 256113

Pourquoi GCC et Clang pense qu'ils ont raison

K, ce qui est le injecté nom de la classe, a une double nature dans le champ d'application de l' K<int>. Vous pouvez l'utiliser sans arguments de modèle. Il se réfère à l' K<int> (pour son propre type).

Il peut être suivi par un modèle de liste d'arguments trop. IMO il est raisonnable de dire que vous avez besoin de le préfixer avec template en raison de l'analyseur ambiguïté avec l' < qui suit. Il se réfère ensuite au type spécifié qui est déterminé par le modèle arguments.

De sorte qu'il peut être traité comme un membre de modèle et comme un type imbriqué, selon qu'elle est suivie par un modèle de liste d'arguments. Bien sûr, K n'est pas vraiment un membre de modèle. La double nature de l'injection nom de la classe me semble plus un hack de toute façon, si.

Le Standard a un exemple qui se lit comme suit:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
   typename Derived::Base b; // error: ambiguous
   typename Derived::Base<double> d; // OK
};

On pourrait être enclin à conclure que l'intention est que vous pouvez laisser de côté l' template. Que dit la Norme

Pour un modèle-nom explicitement qualifiés par les arguments de modèle, le nom doit être connu que de se référer à un modèle.

Je ne vois pas comment cela ne s'applique pas à l' T::K<T>. Si T est un type de charge, alors vous pouvez simplement se pencher en arrière parce que vous ne pouvez pas savoir ce qu' K se réfère lors de l'analyse, donc aucun sens du code, vous avez juste à être en mesure de le préfixer avec template. Notez que n3225 a l'exemple aussi, mais ce n'est pas un défaut, là: Vous pouvez officiellement laisser tomber template si vous recherche dans le modèle de son propre champ d'application en C++0x (cela s'appelle de l ' "instanciation").

Jusqu'à maintenant, Clang et GCC sont beaux.


Pourquoi Comeau est à droite

Juste pour rendre les choses encore plus compliquées, nous devrons considérer les constructeurs d' K<int>. Il y a un constructeur par défaut et un constructeur de copie déclarée implicitement. Un nom K<int>::K va se référer au constructeur(s) de l' K<int> , à moins que la recherche d'un nom utilisé ignore la fonction (constructeur) noms. Va typename T::K ignorer les noms de fonction? 3.4.4/3 dit à propos élaboré spécificateurs de type, qui typename ... est un des:

Si le nom est qualifiée-id, le nom est recherché en fonction de ses qualifications, comme décrit dans 3.4.3, mais en ignorant les non-type de noms qui ont été déclarées.

Cependant, une typename ... utilise différents types de recherche. 14.6/4 dit

L'habitude qualifiés à la recherche d'un nom (3.4.3) est utilisé pour trouver le personnel qualifié-id, même en présence de typename.

L'habitude qualifié de recherche de 3.4.3 ne pas ignorer non-noms de type, comme le montre l'exemple attaché à 14,6/4. Donc, nous allons trouver le constructeur(s) tel que spécifié par 3.4.3.1/1a (la torsion supplémentaire que cela se produit uniquement lorsque la non-types sont pas ignorés, a été ajouté par un plus tard rapport de défaut, qui tous populaire C++03 compilateurs de mettre en œuvre tout de même):

Si le sous-nom-spécificateur désigne une classe C, et le nom spécifié après la nested-nom-prescripteur, quand on les regarde dans C, est la injecté de nom de classe de C (clause 9), le nom est plutôt considéré comme le nom du constructeur de la classe C. un Tel constructeur nom doit être utilisé uniquement dans la déclaration de l'id d'un constructeur définition qui figure à l'extérieur de la définition de la classe.

Donc au final, je pense comeau est correct pour diagnostiquer ce, parce que vous essayez de mettre un modèle de liste d'arguments sur un non-modèle et aussi en violation de l'exigence de cité dans la dernière partie (que vous utilisez le nom d'ailleurs).

Nous allons changer en accédant à la injecté nom par un dérivé de la classe, donc pas de constructeur nom de la traduction se produit, et vous vraiment accéder au type de sorte que vous avez vraiment pouvez ajouter les arguments de modèle:

// just replace struct X with this:
template<typename T>
struct X
{
   struct Derived : T { };
   typename Derived::template K<T> *p;
};

Tout compile maintenant avec comeau trop! Notez que j'ai déjà fait de rapport de problème de bruit à propos de cette chose exacte. Voir Incorrecte constructeur de résolution de nom. BTW, si vous déclarez un constructeur par défaut en K, vous pouvez voir comeau donner un meilleur message d'erreur si vous utilisez T::K<int>

"ComeauTest.c", line 13: error: overloaded function "N::K<T>::K [with T=int]" is
          not a template
     typename T::template K<T> *p;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X