2015-07-10 8 views
12

Nach clang, gcc und vs2013, ist die Funktion Outer::fnicht ein Freund der Klasse Outer::Inner.Die Elementfunktion Outer :: f() ist kein Freund der Klasse Outer :: Inner. Warum?

struct Outer { 
    void f() {} 
    class Inner { 
     friend void f(); 
     static const int i = 0; 
    }; 
}; 

void f() { int i = Outer::Inner::i; } 

Von [namespace.memdef]/3 I die Funktion Outer::f ein Freund von Outer::Inner, statt ::f, zu sein, weil der Freund Erklärung in seinem Namespace nicht die ersten ist erwarten würde den Namen f enthält.

[Namespace, memdef]/3 (Schwerpunkt liegt mir):

Jeder Name zuerst in einem Namespace deklariert ist ein Mitglied dieser Namespace. Wenn ein Freund Erklärung in einer nicht-lokalen Klasse erste eine Klasse deklariert, Funktion, Klasse-Vorlage oder Funktion Vorlage der Freund ist ein Mitglied der innersten umschließenden Namespace. Die Friend-Deklaration selbst macht den Namen nicht sichtbar für unqualifiziertes Nachschlagen (3.4.1) oder qualifiziertes Nachschlagen (3.4.3). [Hinweis: Der Name des Freundes wird in seinem Namespace sichtbar, wenn eine übereinstimmende Deklaration im Namespacebereich bereitgestellt wird (entweder vor oder nach der Klassendefinition, die Freundschaft gewährt). - Endnote] Wenn eine Freundes- oder Funktionsvorlage aufgerufen wird, kann der Name durch den Namen lookup gefunden werden, der Funktionen aus Namespaces und Klassen berücksichtigt, die den Typen der Funktion Argumente (3.4.2) zugeordnet sind. Wenn der Name in einer Friend-Deklaration weder qualifiziert ist noch eine Template-ID und die Deklaration eine Funktion oder ein elaborated-type-specifier ist, darf die Suche zur Bestimmung, ob die Entität zuvor deklariert wurde, keine Bereiche außerhalb der Innerster einschließender Namespace.

+0

Der zitierte Text sagt "... das Nachschlagen, um zu bestimmen, ob die Entität zuvor deklariert wurde, darf keine Bereiche ** außerhalb ** des innersten einschließenden Namespace berücksichtigen". Die Frage ist hier, was "draußen" eigentlich gemein ist? Soll es auch mit "außer" oder "neben" sein (in diesem Fall bedeutet die obige Passage, dass die Suche im innersten Namespace und * nirgendwo sonst *) durchgeführt wird? Aber in diesem Fall, wenn ich mich nicht irre, sollte es "außerhalb von" sagen ... Oder soll sich "draußen" auf größere umhüllende Namespaces beziehen, die den innersten umschließen? – AnT

+0

Im letzteren Fall eliminiert "outside" nicht "struct Outer" von der Suche. – AnT

+0

Ich gehe davon aus, dass die Suche im innersten Namespace und nirgendwo sonst für die im Satz erklärten Fälle ausgeführt wird: 'Wenn der Name in einer Friend-Deklaration weder qualifiziert noch eine Template-ID ist und die Deklaration eine Funktion oder ein elaborierter Typ ist -specifier, darf die Suche zur Bestimmung, ob die Entität zuvor deklariert wurde, keine Bereiche außerhalb des innersten umschließenden Namespace berücksichtigen. Dabei ist der innerste einschließende Namespace in diesem Fall der globale Namespace. – Belloc

Antwort

9

Der erste Teil des Standards, die Sie zitiert sagt (Hervorhebung von mir):

Jeder Name zuerst in einem Namespace erklärt ein Mitglied dieser Namespace ist. Wenn eine Friend-Deklaration in einer nicht lokalen Klasse zuerst eine Klasse oder Funktion deklariert, ist die Friend-Klasse oder -Funktion ein Member des innersten einschließenden Namespace.

Sie gehen davon aus, dass eine Klasse dem Namespace entspricht, was nicht korrekt ist.

namespace Outer { 
    void f(); 
    class Inner { 
     friend void f(); 
     static const int i = 0; 
    }; 
} 

void Outer::f() { int i = Outer::Inner::i; } 

sollte funktionieren. Um die Klassenmitgliedsfunktion als Freund zu verwenden, müssen Sie Folgendes verwenden:

struct Outer { 
    void f(); 
    class Inner { 
     friend void Outer::f(); 
     static const int i = 0; 
    }; 
}; 

void Outer::f() { int i = Outer::Inner::i; } 
+0

Nach [namespace.memdef]/3 ist das nicht notwendig. Eine Suche nach dem Namen "f", der in der Friend-Deklaration gestartet wurde, findet den Namen Outer :: f. – Belloc

+0

@Belloc, der Beispielcode, der in diesen Abschnitten vorgestellt wird, hat 'Namespace A' als den einschließenden' Namespace'. –

+0

Ich sehe das OP diese Annahme nirgendwo machen. Welcher Teil des Standardtextes besagt, dass nur die Namespace-Bereiche und nicht die Klassenbereiche berücksichtigt werden sollten? – AnT

2

Nach [Namespace.memdef]:

Wenn der Name in einer friend Erklärung weder qualifiziert noch ein Template-id ist und die Erklärung ist eine Funktion oder ein erarbeiteter Typ-Spezifizierer, um die Suche zu bestimmen, ob das Unternehmen zuvor erklärt wurde soll keine Bereiche außerhalb der innersten umschließenden Namensraum berücksichtigen.

Was bedeutet "draußen"? Es könnte bedeuten, dass (1) extern von (wie in allen Bereichen innerhalb des innersten einschließenden Namespaces erlaubt ist, aber keine anderen) oder es (2) ausschließlich (wie in, nur der innerste einschließende Namespace berücksichtigt wird) gemeint ist. Die Formulierung ist möglicherweise mehrdeutig. Jedoch betrachten Sie dieses Beispiel, die von OP ursprünglichen Frage und OP Kommentare zusammengeführt wird:

struct Outer { 
    void f() { } 
    class C { void foo(); }; 

    class Inner { 
     friend class C; 
     friend void f(); 
     static const int i = 0; 
    }; 
}; 

void f() { (void)Outer::Inner::i; }    // compiles on GCC,Clang 
void Outer::C::foo() { (void)Outer::Inner::i; } // compiles on GCC,Clang 

int main() { } 

Basierend auf Formulierung (1), Outer::f und Outer::C sollten Freunde von Inner sein. Basierend auf dem Wortlaut (2), ::f und ::C sollten die Freunde sein. Die eine oder andere Interpretation könnte Sinn ergeben, jedoch enden sowohl GCC als auch Clang mit ::f und Outer::C als Freunde, was offensichtlich keinen Sinn ergibt. Ich habe GCC Bug 66836 und Clang Bug 24088 eingereicht. Entweder sind beide Compiler in der einen oder anderen Richtung falsch, oder es gibt einen Teil des Standards, der diese Logik erklärt, die mir definitiv entgeht. Ich würde nicht gegen Letzteres wetten.

+2

Ich bin mir nicht sicher, was Sie sagen wollen, indem Sie betonen, dass "... keine Bereiche außerhalb des innersten umschließenden Namensraums berücksichtigt werden". Der Begriff "außen" bezieht sich auf weitere "größere" Namensräume, die den innersten umschließenden Namespace "einwickeln" (falls vorhanden). Der Geltungsbereich von "struct Outer", den das OP in Betracht zieht, ist * nicht außerhalb * des innersten einschließenden Namensraums. I.e. Die hervorgehobene Passage scheint für die Frage des OP nichts Relevantes zu ändern. – AnT

+0

@AnT @AnT Es beantwortet genau die Frage von OP. Außerhalb von kann auch exklusiv bedeuten. Der betrachtete singuläre Bereich ist der innerste umschließende Namespace. Was ist '::' und nicht ':: Outer'. – Barry

+0

Unter dieser Annahme "beantwortet" die Frage "OP" nur genau die beabsichtigte Bedeutung von "draußen". Und das ist das Problem. Was bedeutet "draußen" in diesem Fall? Sind Bereiche verschachtelt * innerhalb * des innersten Namespace-Bereichs, der in der beabsichtigten Bedeutung des Begriffs auch "außerhalb" betrachtet wird? – AnT