2010-08-23 10 views
7

Unten ist eine rein akademisch erfundene Klassenhierarchie.Was ist das erwartete Verhalten?

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(){} 

Ich erwartete Erklärung für X :: f2 mit sein zweideutig als 'X' ist ein mehrdeutiger Basis von 'D' (visbility vs Zugänglichkeit von X). Aber g ++ (ideone.com) kompiliert es gut.

Ich überprüft mit Online Comeau und es gibt Fehler bei der Verwendung von Deklaration für X :: f2 wie erwartet. Es ergibt jedoch eine Mehrdeutigkeit für die Verwendung der Deklaration für Z :: X :: f3.

Was ist das erwartete Verhalten?

Edit 1:

Ein Verweis auf den entsprechenden Abschnitt der Norm wäre hilfreich, bitte.

Edit 2:

Ich habe mit VS 2010 und es hat Einwände nur mit der Verwendung Erklärung X :: f2. Es handelt sich jedoch nicht um eine Mehrdeutigkeit von "X" (wie im Fall von gcc und Comeau). Es geht um "Fehler C2876: 'X': nicht alle Überladungen sind zugänglich".

bearbeiten 3:

struct X{ 
    void f(){} 
}; 

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

}; 

struct trouble : X{ 
}; 

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

int main(){} 

Hier habe ich versucht (absichtlich) ein Problem mit Typen in using-Deklaration zu erstellen. Gcc kompiliert dies immer noch und VS2010. Comeau gibt immer noch Fehler (wie erwartet) über zweideutige Typen "Probleme". Geht man von den Erklärungen für die ersten Abfragen aus, scheint GCC und VS2010 falsch zu sein. Ist das korrekt?

+0

In D fügen Sie eine Methode hinzu, die f2() aufruft, um zu sehen, was passiert. –

Antwort

2

Ich glaube nicht, dass diese schlecht geformt sind. Zuerst wird nach using X::f2, X gesucht, und dies ergibt eindeutig die Klassenart X. Dann wird f2 in X nachgeschlagen, und das ist auch eindeutig (es wird nicht in D nachgeschlagen!).

Der zweite Fall funktioniert aus dem gleichen Grund.

Aber wenn Sie Anruff2 auf einem D Objekt, wird der Anruf nicht eindeutig sein, da der Name f2 in allen Subobjekte von D vom Typ X nachgeschlagen und D hat zwei solche Teilobjekte und f2 ist ein nicht -statische Elementfunktion Derselbe Grund gilt für den zweiten Fall. Es macht keinen Unterschied, ob Sie f3 mit Z::X oder X direkt nennen. Beide bezeichnen die Klasse X.

Um eine Mehrdeutigkeit für die Verwendungserklärung zu erhalten, müssen Sie es anders schreiben. Beachten Sie, dass in C++ 0x using ThisClass::...; nicht gültig ist. Es ist jedoch in C++ 03, solange sich der gesamte Name auf ein Basisklassenmitglied bezieht.

Umgekehrt, wenn dies in C++ 0x erlaubt wäre, die ganze Erklärung auch mit gültig wäre, weil C++ 0x nicht Subobjekte berücksichtigt für Name-Nachschlag nehmen: D::f2 bezieht sich eindeutig auf nur eine Erklärung (die in X). Siehe DR #39 und die Abschlussarbeit 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; 
}; 

Die C++ 03 Norm beschreibt dies in den Absätzen 10.2 und 3.4.3.1.


Antwort für Edit3:

Ja, GCC und VS2010 falsch ist. trouble bezieht sich auf den Typ, der durch den Namen der eingefügten Klasse ::trouble und die verschachtelte Klasse Y::trouble gefunden wird. Der Name trouble vor dem :: wird mit unqualifizierten Lookup (von 3.4.1/7, die an 10.2 im ersten Aufzählungszeichen delegiert) Nachschlagen aller Objekt, Funktion und Enumerator-Namen (3.4.3/1 - es gibt jedoch keine solche Namen in diesem Fall) nachgeschlagen. Es verletzt dann gegen 10.2 ‚s Anforderung, dass:

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


Es ist möglich, dass VS2010 und GCC anders als Comeau C++ 0x Formulierung interpretieren und rückwirkend diese Formulierung implementieren:

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

Diese bedeutet, dass Nicht-Basisklassen berücksichtigt werden, aber es ist ein Fehler, wenn eine Nicht-Basisklasse benannt wird. Wenn der Standard beabsichtigen würde, Nicht-Basisklassennamen zu ignorieren, würde es sagen kann nur hier, oder buchstabieren Sie es explizit (beide Praktiken sind getan). Der Standard ist jedoch in keiner Weise mit seiner Verwendung von soll und kann. Und GCC implementiert C++ 0x-Formulierung, weil es sonst völlig fehlerfreien C++ 03-Code zurückweist, nur weil die using-Deklaration seinen Klassennamen enthält.

Ein Beispiel für die unklare Formulierung, betrachten den folgenden Ausdruck:

a.~A(); 

Dies ist syntaktisch mehrdeutig, weil es ein Mitglied Funktionsaufruf sein kann, wenn a ein Klassenobjekt ist, aber es kann ein Pseudo sein -destructor-call (was ein No-Op ist), wenn a einen Skalartyp hat (wie int). Aber was die Norm sagt, ist für die Syntax eines pseudo-Destruktoraufrufs und Klassenmitglied Zugriff auf 5.2.4 und 5.2.5 jeweils

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).

, dass die falsche Verwendung ist, weil es nicht die Mehrdeutigkeit überhaupt nicht aufklären. Es sollte "kann nur" verwenden, und Compiler interpretieren es auf diese Weise. Das hat hauptsächlich historische Gründe, wie mir ein Komitee-Mitglied kürzlich im Usenet erzählt hat. Siehe The rules for the structure and drafting of International Standards, Anhang H.

+0

@litb: Ich habe die Klasse 'D' wie folgt modifiziert: struct D: Y, Z {mit X :: f2; mit Z :: X :: f3; void f() {X * p = das;}}; Jetzt habe ich den Fehler prog.cpp: 19: Fehler: wiederholte Verwendung der Deklaration 'mit X :: f3' prog.cpp: In Member-Funktion 'void D :: f()': prog.cpp: 20: Fehler: 'X 'ist eine mehrdeutige Basis von' D ' Sind die Regeln für' Verwendung der Deklaration 'während' Name Lookup ' – Chubsdad

+0

@chubsdad alles, was ich tun kann, ist es, den Standard zu wiederholen. Ich weiß nicht, was diese Compiler implementieren. Ich sehe keine "wiederholte Verwendungserklärung" in Ihrem Code. –

+0

@litb: leichter Tippfehler in meinem Code und daher der Compiler-Ausgang.Der überarbeitete Fehler folgt: Prog.cpp: In Mitgliedsfunktion 'void D :: f()': prog.cpp: 20: Fehler: 'X' ist eine mehrdeutige Basis von 'D' – Chubsdad

0

mit X :: f2; sollte nicht aufgrund von privaten Vererbung von Code unten arbeitet

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

Es ist nicht möglich Konflikte würde also X :: f2 Mitglieder von X bis Y zuzugreifen.

Z::X::f2 sollte funktionieren. Oder sollte funktionieren.

+0

C++ hat die Mehrfachzugriffsregel. Wenn ein Name von mehreren Pfaden in einer Basisklassenhierarchie gefunden werden kann und einer davon öffentlich ist, wird dieser öffentliche Zugriffspfad verwendet. Siehe '11.7/1'. –

+0

@litb: Es bedeutet also, dass 'mit X :: f2' in Ordnung ist, da 'X' über 'Z' in OP erreichbar ist. Aber dann ist "X" ein mehrdeutiger und daher mehrdeutiger Fehler. VS2010 Fehlermeldung über 'nicht alle Überladungen sind zugänglich' ist wahrscheinlich schwierig. Das bedeutet, dass Comeau Recht hat. Ich vermutete am wenigsten den Fehler bei der Verwendung der Deklaration für 'Z :: X :: f3'. – Chubsdad

+0

@chubsdad bedeutet nur, dass es sich nicht um einen Erreichbarkeitsfehler handelt. Es ändert den Zugriff auf den Pfad, der den meisten Zugriff ermöglicht. Es wäre auch ohne 11.7/1 kein mehrdeutiges Nachschlagen, aber dann würden wir nicht wissen, ob wir private oder öffentliche Zugänglichkeit anwenden müssen. –

0

Zuerst, Johannes Antwort zu klären. Wenn Sie using Z::X::f2; sagen, erstellt der Compiler keinen Pfad zu f2, um zu verfolgen, wie auf es zugegriffen werden soll. Da Z::X dasselbe ist wie X, ist die Deklaration genauso wie using X::f2; zu sagen. Vergleichen Sie es mit diesem Beispiel:

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 
}; 

Die Syntax Z::X nicht wegen der Vererbung oder Mitgliedschaft funktioniert, sondern weil die Kennung X vom Umfang Z zugänglich ist. Sie dürfen sogar Z::Z::Z::Z::X::X::X::X ad nauseam schreiben, weil jede Klasse ihren eigenen Namen in ihren eigenen Bereich bringt. So :: hier keine Vererbung ausdrückt.

Jetzt, um das Problem zu lösen. f2 wird von Y und Z von X vererbt. Somit ist es ein erstklassiges Mitglied von Y und Z. E muss nicht über X wissen, weil es ein verstecktes Implementierungsdetail ist. Also, wollen Sie

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

Um in Bezug auf 9.1/2 zu erklären, wie Sie fragen:

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.

Der Name X injiziert in X als X::X. Es wird dann in Y und Z vererbt. Y und Z deklarieren Sie nicht implizit X in ihrem eigenen Umfang.

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.

Bitte beachte, dass ich die mehrere Wortunterobjekte bolded. Obwohl der Name X in zwei Unterobjekten gefunden wird, sind beide vom selben Typ, nämlich X.

+0

Im Rahmen von 'D' gibt es zwei 'X' Suboobjects (eines von 'Y' (unzugänglich) und eines von ' Z '(zugänglich)). Mit Bezug auf $ 9,2 (Injected Klassenname). Sollte' nicht mit X :: f2 'mehrdeutig mit der obigen Logik sein, da' X 'eine mehrdeutige Basis von' D 'ist? – Chubsdad

+0

@chubs: Es gibt nur eine Klasse namens 'X'.' X' benennt eine Klasse, kein Unterobjekt: das ist der Punkt meiner Krankheit Entwicklung. – Potatoswatter

+0

@chubs: wie für §9.1/2 (nicht $ 9.2), siehe aktualisierte Antwort. – Potatoswatter

Verwandte Themen