c++ - sous - telecharger compilateur c




Qu'est-ce qu'un compilateur vérifie le code de modèle non établi? (4)

Au début, si vous instanciez Devired et essayez d'appeler badbar il y aurait une erreur de compilation:

// ...
int main()                                                                      
{                                                                               
    Derived<int> d;
    d.badbar();
    return 0;                                                                   
}

produit

error: too few arguments to function call, single argument 'i' was not specified
void badbar() { Base::badfoo(); }  // compiles ok
                ~~~~~~~~~~~~ ^

Le compilateur ne compile pas le code, qui n'est pas instancié.

Par exemple, le morceau de code suivant compile avec gcc-4.9 et clang-602

class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int i) {}                                                       
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    void bar() { Base::foo(); }                                                 
    void badbar() { Base::badfoo(); }  // compiles ok
    //static void badbar() { Base::badfoo(); }  // compile error                                                                                    
    //void worsebar() { Base::nonexist(); }  // compile error                                   
};                                                                              

int main()                                                                      
{                                                                               
    return 0;                                                                   
}  

Mais les lignes commentées ne seront pas compilées.

Mes questions sont:

  1. Pourquoi badbar() compile mais worsebar() ne le fait pas?

  2. Si je change badbar() en statique, il ne compilera pas non plus, peu importe si base::badfoo est statique ou non.

  3. La norme dit-elle quelque chose sur ce qui doit être vérifié dans cette situation? gcc4.4 refuse en fait de compiler même badbar() .

Mettre à jour:

Le problème 1 a été expliqué par un certain nombre de réponses, mais il semble que les compilateurs fassent parfois un effort supplémentaire pour vérifier la surcharge, ce qui arrive à gcc 4.4.3 et 4.8.2, mais pas 4.7.2 et 4.9.1.

Problème 2: Comme l'a souligné Marco A., Clang ne compilera pas mais gcc4.9 passera quand même. Cependant, gcc4.2 et gcc4.4 rejettent tous les deux le code, et l'erreur qu'ils se plaignent est "pas de fonction correspondante" plutôt que "d'appeler un membre non statique sans objet" dans clang. Il ne semble y avoir aucune réponse concluante à cette question, alors j'ajoute un tag de juriste-avocat comme l'a suggéré Daniel Frey.

Plus de mise à jour:

Je pense que pour la question 2, la réponse est assez claire maintenant: le compilateur doit déterminer si l'ajout d'une déclaration statique changera le diagnostic. Cela varie d'un compilateur à un autre et de différentes versions du même compilateur. L'avocat de la langue ne s'est pas présenté, je vais accepter la réponse de Daniel Frey car elle expliquait mieux la première question. Mais les réponses de Marco A. et Hadi Brais méritent également d’être lues.


Avant d’instancier des types ou d’émettre du code, le compilateur génère de manière incrémentielle une table de tous les symboles qui ont été déclarés. Si un symbole non déclaré a été utilisé, il émet une erreur. C'est pourquoi la pire barre ne sera pas compilée. En revanche, badfoo a été déclaré et badbar compile. À ce stade précoce du processus de compilation, le compilateur ne vérifiera pas si l’appel de badfoo correspond bien au badfoo déclaré.

Le type dérivé n'ayant pas été instancié dans le code, le compilateur n'émettra aucun code le concernant. En particulier, badbar sera juste négligé.

Maintenant, lorsque vous déclarez une instance de Derived (tel que Derived <int>) mais sans utiliser aucun de ses membres, le compilateur va simplement créer un type avec les membres qui ont été utilisés et omettre les autres. Pourtant, aucune erreur concernant badbar.

Cependant, lors de la déclaration d'une instance de badbar Derived et appelant, une instanciation de la méthode badbar serait nécessaire et le compilateur créera un type avec badbar et le compilera. Cette fois, le compilateur remarque que badfoo n'est pas réellement déclaré et émet donc une erreur.

Ce comportement est documenté dans la norme C ++ de la section 14.7.1.

À moins qu'un membre d'un modèle de classe ou d'un modèle de membre n'ait été explicitement instancié ou explicitement spécialisé, la spécialisation du membre est implicitement instanciée lorsque la spécialisation est référencée dans un contexte qui requiert la définition du membre.

Enfin, si badbar était statique et instancié par le compilateur (car il a été utilisé), le compilateur émettra une erreur indiquant que badfoo n’existe pas. Maintenant, si vous passez un argument entier à badfoo, une autre erreur sera émise indiquant qu'une méthode statique ne peut pas accéder à un membre d'instance car il n'y a pas d'instance en premier lieu.

modifier

Le compilateur n'est pas obligé de NE PAS signaler les erreurs sémantiques dans les types de modèles non étayés. La norme dit simplement qu’elle n’a pas à le faire, mais elle le peut. En ce qui concerne où dessiner la ligne est ouverte au débat. Voir this discussion sur un problème connexe en clang:

Quels modèles non étayés analysons-nous? Pour des raisons de performance, je ne pense pas que nous devrions analyser tous les modèles non étayés, car nous pourrions nous retrouver à plusieurs reprises à analyser une grande partie du Boost et du STL, etc.

Ainsi, l'analyse des modèles non corrigés change avec les différentes versions de clang et de gcc de différentes manières. Mais encore une fois, selon la norme: il n’est pas nécessaire de signaler les erreurs dans les modèles non étayés, bien sûr.


La norme exige uniquement que la recherche de nom ait lieu à la phase 1, lors de la première analyse du modèle. Cela badfoo dans badbar ce qui explique pourquoi le code est badbar . Le compilateur n'est pas obligé d'effectuer la résolution de surcharge à ce moment.

La résolution de la surcharge (qui se produit toujours à la suite de la recherche de nom) est ensuite exécutée dans la phase 2 lorsque badbar est instancié - ce qui n’est pas le cas dans votre exemple. Ce principe peut être trouvé dans

3.4 Recherche de nom [basic.lookup]

1 Les règles de recherche de nom s'appliquent uniformément à tous les noms (y compris les noms de typedef (7.1.3), les noms d’espace de noms (7.3) et les noms de classe (9.1)). . La recherche de nom associe l'utilisation d'un nom à une déclaration (3.1) de ce nom. La recherche de nom doit trouver une déclaration non ambiguë pour le nom (voir 10.2). La recherche de nom peut associer plusieurs déclarations à un nom si elle trouve que le nom est un nom de fonction. les déclarations forment un ensemble de fonctions surchargées (13.1). La résolution de surcharge (13.3) a lieu après la recherche du nom. Les règles d'accès (clause 11) ne sont prises en compte qu'une fois la recherche de nom et la résolution de surcharge de fonction (le cas échéant) réussies. Ce n'est qu'après la recherche de nom que la résolution de surcharge de fonction (le cas échéant) et la vérification d'accès ont réussi sont les attributs introduits par la déclaration de nom utilisée ultérieurement dans le traitement des expressions (clause 5).

(Accent mis sur le mien)

Je dirais donc que le ou les compilateurs acceptent le code, même si je ne suis pas sûr qu'ils soient obligés de le faire.

Pour voir le code rejeté , vous devez instancier badbar .


Pour plus de clarté .. cette version du code compile très bien sur clang et gcc

class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int a) {}                                                       
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    void bar() { Base::foo(); }                                                 
    void badbar() { Base::badfoo(); }   
}; 

depuis

[temp.res] / p8

Si aucune spécialisation valide ne peut être générée pour un modèle et que ce modèle n'est pas instancié, le modèle est mal formé, aucun diagnostic n'est requis.

Les deux gcc et clang ne sont pas nécessaires pour diagnostiquer cela . Celui-ci tombe également dans le même cas que ci-dessus (clang émet une erreur, gcc ne le fait pas)

class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int a) {}                                                   
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    void bar() { Base::foo(); }                                                 
    static void badbar() { Base::badfoo(); } 
}; 

Le cas avec

void worsebar() { Base::nonexist(); }

est différent car il viole la recherche de nom [temp.res] / p9

Lors de la recherche de la déclaration d'un nom utilisé dans une définition de modèle, les règles de recherche habituelles (3.4.1, 3.4.2) sont utilisées pour les noms non dépendants

et [temp.res] / p10

Si un nom ne dépend pas d'un paramètre de modèle (tel que défini au 14.6.2), une déclaration (ou un ensemble de déclarations) pour ce nom doit avoir une portée à l'endroit où le nom apparaît dans la définition du modèle.

Déni de responsabilité: tout ce qui précède ne s'applique pas à MSVC qui reporte volontiers tous ces éléments à la deuxième phase de la recherche.

Modifier:

dans le cas

class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int i) {}                                                       
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    static void badbar() { Base::badfoo(); }  // static function

clang déclenche une erreur alors que gcc ne le fait pas . Cela fait partie du premier cas puisque la recherche de nom est réussie mais que clang effectue une vérification supplémentaire: comme badfoo est une fonction membre, il essaie de construire une expression de référence de membre implicite valide. Il attrape ensuite le fait qu'une fonction membre est implicitement appelée à partir d'une fonction statique et détecte la non correspondance de contexte. Ce diagnostic dépend entièrement du compilateur à ce stade (ce ne serait pas le cas d'une instanciation).







language-lawyer