Pourquoi les qualificatifs de nom de classe redondants sont-ils autorisés?

Je suis tombé sur un code comme celui-ci:

struct A { A() {} A(int) {} }; struct B : A { void init(int i); }; void B::init(int i) { A::A(i); // what is this? } int main() { B b; b.init(2); } 

Ceci compilé et exécuté en utilisant la version bêta de VC11 sans erreurs ni avertissements avec / W4.

L’intention apparente est d’appeler B :: init pour réinitialiser le sous-object de base A de B. Je crois qu’il s’agit d’une déclaration de variable pour une nouvelle variable nommée i de type A La compilation avec clang produit des diagnostics:

 ConsoleApplication1.cpp:11:14: warning: declaration shadows a local variable A::A(i); ^ ConsoleApplication1.cpp:10:22: note: previous declaration is here void B::init(int i) { ^ ConsoleApplication1.cpp:11:14: error: redefinition of 'i' with a different type A::A(i); ^ ConsoleApplication1.cpp:10:22: note: previous definition is here void B::init(int i) { ^ 

Il semble curieux que le type puisse être référencé avec la qualification de classe redondante.

De plus, A::A(i) semble être analysé différemment par VS11 et clang / gcc. Si je fais A::A(b) clang et gcc créent une variable b de type A utilisant le constructeur par défaut. VS11 affiche une erreur en disant que b est un identificateur inconnu. VS11 semble parsingr A::A(i) comme la création d’un A temporaire utilisant le constructeur A::A(int) avec i comme paramètre. Lorsque le qualificatif redondant est éliminé, VS parsing la source comme une déclaration de variable comme le font clang et gcc, et produit une erreur similaire à propos de l’observation de la variable i .

Cette différence d’parsing explique pourquoi VS11 s’étouffe avec plus d’un qualificatif supplémentaire; A::A::A::A(i) , et pourquoi, étant donné que clang et gcc peuvent accepter un qualificatif supplémentaire, tout nombre supérieur à un supplémentaire donne le même résultat qu’un extra.

Voici un autre exemple avec les qualificatifs redondants dans un contexte différent. Tous les compilateurs semblent parsingr cela comme une construction temporaire:

 class Foo {}; void bar(Foo const &) {} int main() { bar(Foo::Foo()); } 
  1. Pourquoi les qualificatifs redondants sont-ils autorisés?
  2. Il existe des contextes auxquels les constructeurs peuvent être référencés, tels que la syntaxe pour hériter des constructeurs ( class D : B { using B::B; }; ) mais VS semble le permettre n’importe où. Est-ce que VS a tort et que clang et gcc ont raison dans la manière dont les qualificateurs redondants sont analysés?
  3. Je sais que VS est toujours assez en retard en termes de conformité aux normes, mais je trouve un peu surprenant que des compilateurs modernes, développés activement, puissent être aussi divergents, résolvant dans ce cas un qualificatif redondant comme nom de les constructeurs n’ont pas de noms) par opposition à la résolution des qualificateurs redondants simplement en types, ce qui conduit VS à construire un temporaire où les autres déclarent une variable. Cela peut être encore pire lorsque B b(A::A(i)); est analysé par clang et gcc comme l’parsing la plus frustrante, mais VS le considère comme une déclaration de variable b de type B avec un initialiseur. Y a-t-il encore beaucoup de différences aussi graves?
  4. Il est clair que les qualificatifs redondants doivent être évités dans le code portable. Y a-t-il un bon moyen d’empêcher l’utilisation de cette construction?

Comme le phénomène peut probablement être atsortingbué à l’ injection de nom de classe , comme indiqué dans la réponse de l’éphémien, cet exemple spécifique a été interdit par le langage C ++ il y a longtemps.

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#147

La combinaison A::A doit faire référence au constructeur de la classe et non au nom de la classe injectée. Le A::A(i) est supposé être interprété par un compilateur conforme comme une expression illégale (et donc sans signification) impliquant un nom de constructeur. Comeau Comstackr, par exemple, refusera de comstackr votre code pour cette raison.

Apparemment, VC11 continue de traiter A::A comme une référence au nom de classe injecté. Chose intéressante, je n’observe pas ce problème dans VS2005.

À l’époque où A::A était interprété comme faisant référence au nom injecté, on pouvait déclarer un object A comme

 A::A::A::A::A::A a; 

et ainsi de suite, avec un nombre arbitraire de A s. Mais plus maintenant. Étonnamment, la version de GCC (4.3.4?) Utilisée par ideone souffre toujours de ce problème

http://ideone.com/OkR0F

Vous pouvez essayer ceci avec votre version de VC11 et voir si cela le permet.

Extrait du projet final ISO / CEI 14882: 2011, §9,

² Le nom de la classe est inséré dans la scope dans laquelle il est déclaré immédiatement après avoir vu le nom de la classe . Le nom de la classe est également inséré dans la scope de la classe elle-même, il s’agit du nom de la classe injectée .

Quand tu écris

 A::A(i); 

c’est la même chose que la déclaration

 A i; 

car les parenthèses supplémentaires sont superflues (vous pouvez en append autant que vous le souhaitez) et A::A fait référence à A


À partir du § 14.6.1,

¹ Comme les classes normales (non modèles), les modèles de classe ont un nom de classe injecté (clause 9). Le nom de classe injecté peut être utilisé comme nom de modèle ou nom de type . Lorsqu’il est utilisé avec une liste de modèles d’arguments , en tant qu’argument de modèle pour un paramètre de modèle , ou en tant qu’identificateur final dans le spécificateur de type élaboré d’une déclaration de modèle de classe ami, il fait référence au modèle de classe lui-même. . Sinon, cela équivaut au nom de modèle suivi des parameters de modèle du modèle de classe entre <> .

Le nom de classe injecté semble être une commodité pour permettre à A<...> d’être désigné simplement par A dans la classe.