2010-08-23 28 views
7

Vous trouverez ci-dessous une hiérarchie de classe purement académique.quel est le comportement attendu?

struct X{ 
     void f1(); 
     void f2(); 
     void f3(); 
}; 

struct Y : private X{ 
     void f4(); 
}; 

struct Z : X{ 
}; 

struct D : Y, Z{ 
     using X::f2; 
     using Z::X::f3; 
}; 

int main(){} 

Je me attendais à l'aide de la déclaration pour X :: f2 ambiguë que 'X' est une base ambiguë de 'D' (visbility vs accessibilité de X). Cependant g ++ (ideone.com) le compile bien.

J'ai vérifié avec Comeau en ligne et il donne l'erreur dans l'utilisation de la déclaration pour X :: f2 comme prévu. Cependant, cela donne une ambiguïté à l'utilisation de la déclaration pour Z :: X :: f3.

Alors, quel est le comportement attendu?

Edit 1:

Une référence à la section appropriée de la norme serait utile, s'il vous plaît.

Edit 2:

J'ai vérifié avec VS 2010 et il a des objections que avec la déclaration en utilisant X :: f2. Cependant il ne s'agit pas d'ambiguïté de 'X' (comme dans le cas de gcc et de Comeau). Il s'agit de "l'erreur C2876: 'X': toutes les surcharges ne sont pas accessibles".

Edit 3:

struct X{ 
    void f(){} 
}; 

struct Y : X{ 
    struct trouble{ 
     void f(){} 
    }; 

}; 

struct trouble : X{ 
}; 

struct letscheck : Y, trouble{ 
    using trouble::f; 
}; 

int main(){} 

Ici, j'ai essayé (volontairement) pour créer un problème avec les types en utilisant la déclaration. Gcc compile encore cette amende et VS2010 aussi. Comeau donne encore une erreur (comme prévu) sur les types ambigus 'trouble'. En suivant les explications données pour les requêtes initiales, il apparaît que GCC et VS2010 sont faux. Est-ce exact?

+0

Dans D, ajoutez une méthode qui appelle f2() pour voir ce qui se passe. –

Répondre

2

Je ne pense pas que ces éléments sont mal formés. Tout d'abord, pour using X::f2, X est recherché, et cela donnera sans ambiguïté le type de classe X. Puis f2 dans X est recherché, et ceci est non ambigu aussi (il n'est pas recherché dans D!).

Le second cas travaillera pour la même raison.

Mais si vous appelf2 sur un objet D, l'appel sera ambigu parce que le nom f2 est recherché dans tous les sous-objets de D de type X et D a deux de ces sous-objets, et f2 est un non -statique fonction membre. La même raison vaut pour le second cas. Cela ne fait pas de différence pour ce que vous nommez f3 directement en utilisant Z::X ou X. Les deux désignent la classe X.

Pour obtenir une ambiguïté pour la déclaration using, vous devez l'écrire différemment. Notez que dans C++ 0x using ThisClass::...; n'est pas valide. C'est en C++ 03 cependant, tant que le nom entier fait référence à un membre de classe de base.

À l'inverse, si cela serait permis en C++ 0x, toute déclaration à l'aide serait également valable, parce que C++ 0x ne prend pas en compte pour les sous-objets de recherche de nom: D::f2 se réfère clairement à une seule déclaration (celui au X). Voir DR #39 et le document final N1626.

struct D : Y, Z{ 
    // ambiguous: f2 is declared in X, and X is a an ambiguous base class 
    using D::f2; 

    // still fine (if not referred to by calls/etc) :) 
    using Z::X::f3; 
}; 

struct E : D { 
    // ambiguous in C++03 
    // fine in C++0x (if not referred to by an object-context (such as a call)). 
    using D::f2; 
}; 

Le 03 C++ standard décrit ce aux paragraphes 10.2 et 3.4.3.1.


Réponse pour Edit3:

Oui, GCC et VS2010 sont faux. trouble se réfère au type trouvé par le nom de classe injecté de ::trouble et à la classe imbriquée trouvée comme Y::trouble. Le nom trouble précédant le :: est recherché en utilisant la recherche non qualifiée (par 3.4.1/7, qui délègue à 10.2 dans la première puce) en ignorant les noms d'objet, de fonction et d'énumérateur (3.4.3/1 - il n'y a pas de noms dans ce cas, cependant). Il viole alors contre l » exigence 10.2 que:

If the resulting set of declarations are not all from sub-objects of the same type ... the program is ill-formed.


Il est possible que VS2010 et GCC interprètent libellé C++ 0x différemment que Comeau et mettre en œuvre rétroactivement libellé:

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined.

Cette signifie que les classes autres que les bases sont considérées comme, mais que c'est une erreur si une classe autre que la base est nommée. Si le standard aurait l'intention d'ignorer les noms de classes autres que de base, il dirait que peut seulement ici, ou l'épeler explicitement (les deux pratiques sont terminées). La norme cependant n'est pas du tout conséquent avec son utilisation de doit et peut. Et GCC implémente le langage C++ 0x, car il rejette le code C++ 03 par ailleurs tout à fait correct, juste parce que la déclaration using contient son nom de classe.

Pour un exemple de la formulation peu claire, tenez compte de l'expression suivante:

a.~A(); 

Ceci est syntaxiquement ambiguë, car il peut être un appel de fonction de membre si a est un objet de classe, mais il peut être un pseudo -destructor-call (qui est un no-op) si a a un type scalaire (tel que int). Mais ce que la norme dit est la syntaxe d'un appel pseudo-destructor et l'accès des membres de la classe à 5.2.4 et 5.2.5 respectivement

The left-hand side of the dot operator shall be of scalar type.

For the first option (dot) the type of the first expression (the object expression) shall be “class object” (of a complete type).

C'est le mauvais usage, car il ne voit pas clairement l'ambiguïté du tout. Il devrait utiliser "can only", et les compilateurs l'interprètent de cette façon. Cela a principalement des raisons historiques, comme l'a récemment dit un membre du comité sur Usenet. Voir The rules for the structure and drafting of International Standards, Annexe H.

+0

@litb: J'ai modifié la classe 'D' comme suit: struct D: Y, Z {using X :: f2; en utilisant Z :: X :: f3; void f() {X * p = ceci;}}; Maintenant, j'ai l'erreur prog.cpp: 19: erreur: répétée en utilisant la déclaration 'using X :: f3' prog.cpp: Dans la fonction membre 'void D :: f()': prog.cpp: 20: erreur: 'X 'est une base ambiguë de' D ' Les règles sont-elles différentes pour' using declaration 'lors de' name lookup ' – Chubsdad

+0

@chubsdad tout ce que je peux faire est de répéter la norme. Je ne sais pas ce que ces compilateurs implémentent. Je ne vois pas de "déclaration d'utilisation répétée" dans votre code. –

+0

@litb: légère faute de frappe dans mon code et donc la sortie du compilateur.L'erreur révisée suit: prog.cpp: Dans la fonction membre 'void D :: f()': prog.cpp: 20: erreur: 'X' est une base ambiguë de 'D' – Chubsdad

0

en utilisant X :: f2; ne devrait pas travailler en raison de l'héritage privé ci-dessous le code

struct Y : private X{ 
    void f4(); 
}; 

Il est impossible d'accéder à des membres de X par Y. SOx :: f2 serait conflits.

Z::X::f2 devrait fonctionner. Ou Z::f2 devrait fonctionner.

+0

C++ a la règle d'accès multiple. Si un nom peut être trouvé par plusieurs chemins dans une hiérarchie de classes de base, et que l'un d'entre eux est public, ce chemin d'accès public est pris. Voir '11.7/1'. –

+0

@litb: Cela signifie donc que 'utiliser X :: f2' est bien car 'X' est accessible via 'Z' dans OP. Mais alors 'X' est ambigu et donc erreur liée à l'ambiguïté. Donc, le message d'erreur VS2010 sur "pas toutes les surcharges sont accessibles" est probablement difficile. Cela signifie que Comeau a raison. J'ai le moins suspecté l'erreur sur l'utilisation de la déclaration pour 'Z :: X :: f3'. – Chubsdad

+0

@chubsdad cela signifie simplement que ce n'est pas une erreur d'accessibilité. Il change l'accès au chemin qui donne le plus d'accès. Ce ne serait pas une recherche ambiguë, même sans 11.7/1, mais nous ne saurions alors pas si nous devons appliquer l'accessibilité privée ou publique. –

0

D'abord, pour clarifier la réponse de Johannes. Quand vous dites using Z::X::f2;, le compilateur ne "construit pas un chemin" à f2 pour garder une trace de comment il devrait être accédé. Puisque Z::X est la même chose que X, la déclaration est exactement la même chose que dire using X::f2;. Contraste avec cet exemple:

struct A { void f() {} void g() {} }; 
struct B { void f() {} void g() {} }; 
struct C { typedef A X; }; 
struct D { typedef B X; }; 
struct E : A, B { 
    using C::X::f; // C::X == A 
    using D::X::g; // D::X == B 
}; 

La syntaxe Z::X fonctionne pas à cause de l'héritage ou l'appartenance, mais parce que l'identifiant X est accessible à partir de la portée Z. Vous êtes même autorisé à écrire Z::Z::Z::Z::X::X::X::X ad nauseam, car chaque classe apporte son propre nom dans sa propre portée. Ainsi :: ici n'exprime pas l'héritage.

Maintenant, pour résoudre le problème. f2 est hérité par Y et Z de X. Ainsi, il s'agit d'un membre de première classe de Y et Z. E n'a pas besoin de connaître X car il s'agit d'un détail d'implémentation masqué. Alors, vous voulez

struct D : Y, Z{ 
    using Y::f2; // error: inaccessible 
    using Z::f3; 
}; 

Pour expliquer en termes de 9.1/2 que vous demandez:

A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.

Le nom X est injecté dans X comme X::X. Il est ensuite hérité en Y et Z. Y et Z ne déclarent pas implicitement X dans leur propre portée.

10,2/2:

The following steps define the result of name lookup in a class scope, C. First, every declaration for the name in the class and in each of its base class sub-objects is considered. … If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup.

Notez que le mot je pluriel gras sous-objets. Bien que le nom X se trouve dans deux sous-objets, ils sont tous deux du même type, à savoir X.

+0

Dans le cadre de 'D', il y a deux sous-objets 'X' (un de 'Y' (inaccessible) et un de ' Z '(accessible)) En référence à $ 9.2 (Nom de la classe injectée) Ne devrait-il pas être ambigu avec la logique ci-dessus puisque' X 'est une base ambiguë de' D '? – Chubsdad

+0

@chubs: Il y a seulement une classe nommée 'X'.' X' nomme une classe, pas un sous-objet: c'est le point de ma maladie ustration. – Potatoswatter

+0

@chubs: comme pour §9.1/2 (pas $ 9.2), voir la mise à jour de la réponse. – Potatoswatter