Suche nach merkwürdigen Funktionen der geschachtelten Klasse: umgebende Klassenfunktionen verstecken globale Funktionen

8

Ich habe den folgenden vereinfachten Code

%Vor%

Und ich habe diesen Fehler:

  

error: kann die Elementfunktion 'int Namespace :: Class :: foo () const' nicht aufrufen   ohne Objekt:

%Vor%

Es scheint, dass der Compiler die nicht statische int Namespace::Class::foo() const anstelle der globalen Funktion int Namespace::foo() auswählt.

Aber wie kann man erwarten, dass nicht-statische Funktion von einer anderen Klasse ohne Objekt aufgerufen werden kann? Nested object hat keinen Zugriff auf den umgebenden Class object - das ist doch nicht Java.

Ich lese sorgfältig Überladungsauflösung von cppreference Ich kann die Gründe für dieses Verhalten nicht finden. Ich bezweifle, dass dies ein GCC-Fehler ist.

  • Können Sie auf die Sprachregeln verweisen, die für dieses Verhalten verantwortlich sind?
  • Und wie gehen Sie mit solchen Problemen um?

[UPDATE]

Nur eine Antwort für die zweite Frage. Umgehung ist einfach, es ist notwendig, dem Compiler mitzuteilen, dass eine solche globale Funktion existiert:

%Vor%

Übrigens, ist diese Problemumgehung korrekt? Gibt es bessere Lösungen?

    
PiotrNycz 10.12.2014, 13:30
quelle

3 Antworten

5
  

Ich habe die Überladungsauflösung von cppreference sorgfältig gelesen. Ich kann die Gründe für dieses Verhalten nicht finden. Können Sie auf die für dieses Verhalten verantwortlichen Sprachregeln verweisen?

Bevor die Prozedur Überladungsauflösung die beste funktionsfähige Funktion auswählt, wird während der -Namensuche -Phase eine erste Menge von Kandidaten generiert. Mit anderen Worten, das erwartete Verhalten sollte im Abschnitt Name-Suche gesucht werden, nicht im Überladungsauflösung .

Die Namenssuchprozedur für einen nicht qualifizierten Namen wird im C ++ - Standard beschrieben:

§3.4.1 [basic.lookup.unqual] / p8:

  

Ein Name, der in der Definition einer Elementfunktion (9.3) der Klasse X nach der deklarator-ID der Funktion oder in der geschweiften Klammer oder verwendet wird -equal-initializer eines nicht statischen Datenmembers (9.2) der Klasse X muss auf eine der folgenden Arten deklariert werden:

     

- vor seiner Verwendung in dem Block, in dem es verwendet wird oder in einem umschließenden Block (6.3) oder

     

- soll ein Mitglied der Klasse X sein oder ein Mitglied einer Basisklasse von X (10.2) oder

sein      

- wenn X eine verschachtelte Klasse der Klasse Y (9.7) ist, ein Mitglied von Y sein oder Mitglied einer Basisklasse von Y sein soll   (Diese Suche bezieht sich wiederum auf umschließende Klassen von Y , beginnend mit der innersten umschließenden Klasse) oder [...]

und nur wenn noch nicht gefunden:

  

- Wenn X ein Mitglied des Namespace N ist oder eine geschachtelte Klasse einer Klasse ist, die Mitglied von N ist oder eine lokale Klasse oder eine geschachtelte Klasse ist innerhalb einer lokalen Klasse einer Funktion, die ein Mitglied von N ist, vor der Verwendung des Namens im Namespace N oder in einem von N 's umschließenden Namespaces.

Da die Namenssuche endet, sobald der Name gefunden wurde (§3.4.1 [basic.lookup.unqual] / p1):

  

In allen Fällen, die in 3.4.1 aufgelistet sind, werden die Bereiche nach einer Deklaration in der Reihenfolge gesucht, die in jeder der jeweiligen Kategorien aufgeführt ist; Die Namenssuche endet, sobald eine Deklaration für den Namen gefunden wird.

in Ihrem Fall werden keine anderen Bereiche gesucht, sobald int foo() const { return 2; } gefunden wurde.

  

Problemumgehung ist einfach, es ist notwendig, dem Compiler mitzuteilen, dass eine solche globale Funktion existiert:

%Vor%      

Ist diese Problemumgehung korrekt?

§7.3.3 [namespace.udecl] / p1:

  

Eine using-Deklaration führt einen Namen in die deklarative Region ein, in der die using-Deklaration erscheint.

§3.3.1 [basic.scope.declarative] / p1:

  

Jeder Name wird in einen Teil des Programmtexts eingeführt, der als deklarative Region bezeichnet wird. Dies ist der größte Teil des Programms, in dem dieser Name gültig ist. in dem dieser Name als ein unqualifizierter Name verwendet werden kann, um auf dieselbe Entität zu verweisen.

Die Einführung eines Namens mit einer using-Deklaration wirkt sich auf die unqualifizierte Namenssuche so aus, dass sie diese Funktion in ihrem ersten Schritt findet, nämlich dass dieser Name zu deklariert :

  

- vor seiner Verwendung in dem Block, in dem es verwendet wird oder in einem umschließenden Block (6.3)

  

Gibt es bessere Lösungen?

Man kann einen qualifizierten Namen verwenden, wenn er sich auf eine Funktion aus einem Namespace-Bereich bezieht und explizit angibt, auf welches Symbol verwiesen wird:

%Vor%     
Piotr Skotnicki 10.12.2014, 15:17
quelle
4

In der Tat ging es um die Namenssuche und nicht um die Überladungsauflösung.

Wie der Standard festlegt, §3.4.1 / 1 von N3376 (Hervorhebung von mir):

  

In allen Fällen, die in 3.4.1 aufgelistet sind, werden die Bereiche nach einer Deklaration in der Reihenfolge gesucht, die in jeder der jeweiligen Kategorien aufgeführt ist; Die -Namensuche endet, sobald eine Deklaration für den Namen gefunden wird. Wenn keine Deklaration gefunden wird, ist das Programm schlecht formatiert.

In der Tat stoppt die Suche, sobald eine Deklaration gefunden wird.

Dann können Sie mehr über den Fall, mit dem Sie es zu tun haben, in §3.4.1 / 8 finden, der sich mit Namen beschäftigt, die in der Klasse innerhalb einer Memberfunktionsdefinition verwendet werden, insbesondere diesen Teil:

  

wenn X eine geschachtelte Klasse der Klasse Y (9.7) ist, ein Mitglied von Y oder ein Mitglied einer Basisklasse von Y sein soll (diese Suche gilt wiederum für die einschließenden Klassen von Y, beginnend mit dem innersten Einschluss) Klasse)

Und falls nicht gefunden, in umschließenden Namespaces.

In Ihrem Fall bedeutet das, dass Namespace::Class::foo() der erste Name ist, der während der Suche gefunden wurde, und er stoppt, sobald er einen Namen gefunden hat, also wird Namespace::foo() nicht einmal berücksichtigt.

Das Beispiel des Standards veranschaulicht den Suchpfad:

%Vor%
  

Die folgenden Bereiche werden nach einer Deklaration von i durchsucht:

     

1) äußerster Blockbereich von M :: N :: X :: f, vor der Verwendung von i // 2) Bereich der Klasse M :: N :: X

     

3) Umfang der Basisklasse B von M :: N :: X

     

4) Geltungsbereich des Namensraums M :: N

     

5) Geltungsbereich des Namensraums M

     

6) globaler Gültigkeitsbereich vor der Definition von M :: N :: X :: f

Und wie Sie mit einem solchen Problem umgehen, müssen Sie den Anruf qualifizieren, d. h.

%Vor%

, damit der Compiler die gewünschte Funktion durch qualifizierte Suche auswählt.

    
JBL 10.12.2014 15:31
quelle
2

Piotr S. und JBL haben das Warum Ihres Problems erklärt.

IMHO die einfachere Lösung ist, den qualifizierten Namen der Funktion zu verwenden:

%Vor%     
Serge Ballesta 10.12.2014 17:36
quelle