Unqualified name lookup
Für einen
unqualifizierten
Namen, also einen Namen, der nicht rechts eines Bereichsauflösungsoperators
::
erscheint, untersucht die Namenssuche die
Bereiche
wie unten beschrieben, bis sie mindestens eine Deklaration jeglicher Art findet, woraufhin die Suche beendet wird und keine weiteren Bereiche mehr untersucht werden. (Hinweis: Die Suche aus manchen Kontexten überspringt einige Deklarationen, zum Beispiel ignoriert die Suche des Namens, der links von
::
verwendet wird, Funktions-, Variablen- und Enumeratordeklarationen; die Suche eines Namens, der als Basisklassenspezifizierer verwendet wird, ignoriert alle Nicht-Typ-Deklarationen).
Zum Zweck der unqualifizierten Namenssuche erscheinen alle Deklarationen aus einem durch eine using-Direktive nominierten Namespace so, als wären sie im nächstgelegenen einschließenden Namespace deklariert, der direkt oder indirekt sowohl die using-Direktive als auch den nominierten Namespace enthält.
Die Suche nach unqualifizierten Namen für den Namen links vom Funktionsaufrufoperator (und äquivalenter Operator in einem Ausdruck) wird beschrieben in argument-dependent lookup .
Dateibereich
Für einen Namen, der im globalen (Top-Level-Namespace) Bereich verwendet wird, außerhalb jeglicher Funktion, Klasse oder benutzerdeklarierten Namespaces, wird der globale Bereich vor der Verwendung des Namens überprüft:
int n = 1; // Deklaration von n int x = n + 1; // OK: Suche findet ::n int z = y - 1; // Fehler: Suche schlägt fehl int y = 2; // Deklaration von y
Namespace-Bereich
Für einen Namen, der in einem benutzerdeklarierten Namespace außerhalb einer Funktion oder Klasse verwendet wird, wird dieser Namespace vor der Verwendung des Namens durchsucht, dann der diesen Namespace umschließende Namespace vor der Deklaration dieses Namespace, usw. bis der globale Namespace erreicht ist.
int n = 1; // Deklaration namespace N { int m = 2; namespace Y { int x = n; // OK, Suche findet ::n int y = m; // OK, Suche findet ::N::m int z = k; // Fehler: Suche schlägt fehl } int k = 3; }
Definition außerhalb seines Namensraums
Für einen Namen, der in der Definition einer Namespace-Member-Variable außerhalb des Namespace verwendet wird, erfolgt die Suche auf die gleiche Weise wie für einen Namen, der innerhalb des Namespace verwendet wird:
namespace X { extern int x; // Deklaration, keine Definition int n = 1; // zuerst gefunden } int n = 2; // zweitens gefunden int X::x = n; // findet X::n, setzt X::x auf 1
Definition einer Nicht-Mitgliedsfunktion
Für einen Namen, der in der Definition einer Funktion verwendet wird, entweder in ihrem Körper oder als Teil eines Standardarguments, wobei die Funktion ein Mitglied eines benutzerdeklarierten oder globalen Namespace ist, wird der Block, in dem der Name verwendet wird, vor der Verwendung des Namens durchsucht, dann wird der umschließende Block vor dem Beginn dieses Blocks durchsucht usw., bis der Block erreicht wird, der den Funktionskörper darstellt. Dann wird der Namespace, in dem die Funktion deklariert ist, bis zur Definition (nicht notwendigerweise der Deklaration) der Funktion, die den Namen verwendet, durchsucht, dann die umschließenden Namespaces usw.
namespace A { namespace N { void f(); int i = 3; // gefunden als 3. (wenn 2. nicht vorhanden) } int i = 4; // gefunden als 4. (wenn 3. nicht vorhanden) } int i = 5; // gefunden als 5. (wenn 4. nicht vorhanden) void A::N::f() { int i = 2; // gefunden als 2. (wenn 1. nicht vorhanden) while (true) { int i = 1; // gefunden als 1.: Suche ist abgeschlossen std::cout << i; } } // int i; // nicht gefunden namespace A { namespace N { // int i; // nicht gefunden } }
Klassendefinition
Für einen Namen, der überall in der Klassendefinition verwendet wird (einschließlich Basisklassenspezifizierer und verschachtelte Klassendefinitionen), außer innerhalb eines Member-Funktionskörpers, eines Standardarguments einer Member-Funktion, einer Ausnahmespezifikation einer Member-Funktion oder eines Standard-Member-Initialisierers, wobei der Member zu einer verschachtelten Klasse gehören kann, deren Definition sich im Körper der einschließenden Klasse befindet, werden die folgenden Geltungsbereiche durchsucht:
Für eine friend -Deklaration verläuft die Suche zur Bestimmung, ob sie auf eine zuvor deklarierte Entität verweist, wie oben beschrieben, mit der Ausnahme, dass sie nach dem innersten umschließenden Namespace stoppt.
namespace M { // const int i = 1; // niemals gefunden class B { // static const int i = 3; // als 3. gefunden (wird jedoch Zugriffsprüfung nicht bestehen) }; } // const int i = 5; // als 5. gefunden namespace N { // const int i = 4; // als 4. gefunden class Y : public M::B { // static const int i = 2; // als 2. gefunden class X { // static const int i = 1; // als 1. gefunden int a[i]; // Verwendung von i // static const int i = 1; // niemals gefunden }; // static const int i = 2; // niemals gefunden }; // const int i = 4; // niemals gefunden } // const int i = 5; // niemals gefunden
Eingefügter Klassenname
Für den Namen einer Klasse oder eines Klassentemplates, der innerhalb der Definition dieser Klasse oder dieses Templates oder einer davon abgeleiteten verwendet wird, findet die unqualified name lookup die Klasse, die definiert wird, als ob der Name durch eine Member-Deklaration (mit public member access) eingeführt wurde. Für weitere Details siehe injected-class-name .
Definition der Member-Funktion
Für einen Namen, der innerhalb eines Member-Funktionskörpers, eines Standardarguments einer Member-Funktion, einer Exception-Spezifikation einer Member-Funktion oder eines Standard-Member-Initialisierers verwendet wird, sind die durchsuchten Geltungsbereiche dieselben wie in der Klassendefinition , mit der Ausnahme, dass der gesamte Geltungsbereich der Klasse betrachtet wird, nicht nur der Teil vor der Deklaration, die den Namen verwendet. Für geschachtelte Klassen wird der gesamte Körper der umschließenden Klasse durchsucht.
class B { // int i; // gefunden als 3. }; namespace M { // int i; // gefunden als 5. namespace N { // int i; // gefunden als 4. class X : public B { // int i; // gefunden als 2. void f(); // int i; // ebenfalls gefunden als 2. }; // int i; // gefunden als 4. } } // int i; // gefunden als 6. void M::N::X::f() { // int i; // gefunden als 1. i = 16; // int i; // nie gefunden } namespace M { namespace N { // int i; // nie gefunden } }
- In jedem Fall gelten beim Untersuchen der Basen, von denen die Klasse abgeleitet ist, die folgenden Regeln, manchmal auch als Dominanz in der virtuellen Vererbung bezeichnet:
Ein Member-Name, der in einem Subobjekt
B
gefunden wird, verdeckt denselben Member-Namen in jedem Subobjekt
A
, falls
A
ein Basisklassen-Subobjekt von
B
ist. (Beachten Sie, dass dies den Namen in keinen zusätzlichen, nicht-virtuellen Kopien von
A
im Vererbungsgitter verdeckt, die keine Basen von
B
sind: Diese Regel hat nur bei virtueller Vererbung einen Effekt.) Namen, die durch using-Deklarationen eingeführt werden, werden als Namen in der Klasse behandelt, die die Deklaration enthält. Nach der Überprüfung jeder Basis muss der resultierende Satz entweder Deklarationen eines static-Members aus Subobjekten desselben Typs oder Deklarationen von non-static-Members aus demselben Subobjekt enthalten.
|
(bis C++11) |
Ein
Lookup-Set
wird konstruiert, das aus den Deklarationen und den Subobjekten besteht, in denen diese Deklarationen gefunden wurden. Using-Deklarationen werden durch die Member, die sie repräsentieren, ersetzt und Typdeklarationen, einschließlich injizierter Klassennamen, werden durch die Typen ersetzt, die sie repräsentieren. Wenn
C
die Klasse ist, in deren Gültigkeitsbereich der Name verwendet wurde, wird
C
zuerst untersucht. Wenn die Liste der Deklarationen in
C
leer ist, wird für jede ihrer direkten Basen
Bi
eine Lookup-Set erstellt (unter rekursiver Anwendung dieser Regeln, falls
Bi
eigene Basen hat). Nach der Erstellung werden die Lookup-Sets für die direkten Basen wie folgt in die Lookup-Set in
C
zusammengeführt:
|
(seit C++11) |
struct X { void f(); }; struct B1: virtual X { void f(); }; struct B2: virtual X {}; struct D : B1, B2 { void foo() { X::f(); // OK, ruft X::f auf (qualifizierte Suche) f(); // OK, ruft B1::f auf (unqualifizierte Suche) } }; // C++98-Regeln: B1::f verdeckt X::f, daher wird X::f, obwohl es von D aus // über B2 erreichbar ist, durch Namenssuche von D aus nicht gefunden. // C++11-Regeln: Suchmenge für f in D findet nichts, fährt mit Basen fort // Suchmenge für f in B1 findet B1::f und wird abgeschlossen // Merge ersetzt die leere Menge, jetzt enthält Suchmenge für f in C B1::f in B1 // Suchmenge für f in B2 findet nichts, fährt mit Basen fort // Suche für f in X findet X::f // Merge ersetzt die leere Menge, jetzt enthält Suchmenge für f in B2 X::f in X // Merge in C stellt fest, dass jedes Subobjekt (X) in der Suchmenge in B2 eine Basis // jedes bereits gemergten Subobjekts (B1) ist, daher wird die B2-Menge verworfen // C bleibt nur mit B1::f gefunden in B1 übrig // (wenn struct D : B2, B1 verwendet würde, dann würde der letzte Merge C's // bisher gemergtes X::f in X *ersetzen*, weil jedes bereits zu C hinzugefügte // Subobjekt (nämlich X) eine Basis von mindestens einem Subobjekt in der neuen // Menge (B1) wäre; das Endergebnis wäre dasselbe: Suchmenge in C enthält // nur B1::f gefunden in B1)
-
Unqualifizierte Namenssuche, die statische Member von
B, geschachtelte Typen vonBund inBdeklarierte Enumeratoren findet, ist eindeutig, selbst wenn es mehrere nicht-virtuelle Basis-Subobjekte vom TypBin der Vererbungshierarchie der untersuchten Klasse gibt:
struct V { int v; }; struct B { int a; static int s; enum { e }; }; struct B1 : B, virtual V {}; struct B2 : B, virtual V {}; struct D : B1, B2 {}; void f(D& pd) { ++pd.v; // OK: nur ein v, da nur ein virtuelles Basis-Subobjekt ++pd.s; // OK: nur ein static B::s, obwohl in B1 und B2 gefunden int i = pd.e; // OK: nur ein Enumerator B::e, obwohl in B1 und B2 gefunden ++pd.a; // Fehler, mehrdeutig: B::a in B1 und B::a in B2 }
Friend-Funktionsdefinition
Für einen Namen, der in einer friend -Funktionsdefinition innerhalb des Klassenkörpers verwendet wird, der die Freundschaft gewährt, erfolgt die unqualifizierte Namenssuche auf die gleiche Weise wie für eine Memberfunktion. Für einen Namen, der in einer friend -Funktion verwendet wird, die außerhalb des Klassenkörpers definiert ist, erfolgt die unqualifizierte Namenssuche auf die gleiche Weise wie für eine Funktion in einem Namespace.
int i = 3; // gefunden 3. für f1, gefunden 2. für f2 struct X { static const int i = 2; // gefunden 2. für f1, nie gefunden für f2 friend void f1(int x) { // int i; // gefunden 1. i = x; // findet und modifiziert X::i } friend int f2(); // static const int i = 2; // gefunden 2. für f1 überall im Klassenbereich }; void f2(int x) { // int i; // gefunden 1. i = x; // findet und modifiziert ::i }
Friend-Funktionsdeklaration
Für einen Namen, der im Deklarator einer friend -Funktionsdeklaration verwendet wird, die eine Memberfunktion einer anderen Klasse als Freund deklariert: Wenn der Name nicht Teil eines Template-Arguments im Deklarator -Bezeichner ist, untersucht die unqualifizierte Namenssuche zunächst den gesamten Gültigkeitsbereich der Klasse der Memberfunktion. Wenn er in diesem Gültigkeitsbereich nicht gefunden wird (oder wenn der Name Teil eines Template-Arguments im Deklarator-Bezeichner ist), setzt sich die Suche fort, wie für eine Memberfunktion der Klasse, die die Freundschaft gewährt.
template<class T> struct S; // die Klasse, deren Memberfunktionen als Friend deklariert werden struct A { typedef int AT; void f1(AT); void f2(float); template<class T> void f3(); void f4(S<AT>); }; // die Klasse, die Friend-Zugriff für f1, f2 und f3 gewährt struct B { typedef char AT; typedef float BT; friend void A::f1(AT); // Suche nach AT findet A::AT (AT wurde in A gefunden) friend void A::f2(BT); // Suche nach BT findet B::BT (BT wurde nicht in A gefunden) friend void A::f3<AT>(); // Suche nach AT findet B::AT (keine Suche in A, weil // AT im Deklarator-Identifier A::f3<AT> enthalten ist) }; // die Klassenvorlage, die Friend-Zugriff für f4 gewährt template<class AT> struct C { friend void A::f4(S<AT>); // Suche nach AT findet A::AT // (AT ist nicht im Deklarator-Identifier A::f4 enthalten) };
Standardargument
Für einen Namen, der in einem Default-Argument in einer Funktionsdeklaration verwendet wird, oder einen Namen, der im Ausdruck eines Member-Initialisierers eines Konstruktors verwendet wird, werden die Funktionsparameternamen zuerst gefunden, bevor die umschließenden Block-, Klassen- oder Namensraumbereiche untersucht werden:
class X { int a, b, i, j; public: const int& r; X(int i): r(a), // initialisiert X::r als Referenz auf X::a b(i), // initialisiert X::b mit dem Wert des Parameters i i(i), // initialisiert X::i mit dem Wert des Parameters i j(this->i) // initialisiert X::j mit dem Wert von X::i {} }; int a; int f(int a, int b = a); // Fehler: Suche nach a findet Parameter a, nicht ::a // und Parameter sind als Standardargumente nicht erlaubt
Definition statischer Datenelemente
Für einen Namen, der in der Definition eines static data member verwendet wird, erfolgt die Suche auf die gleiche Weise wie für einen Namen, der in der Definition einer Member-Funktion verwendet wird.
struct X { static int x; static const int n = 1; // zuerst gefunden }; int n = 2; // zweitens gefunden int X::x = n; // findet X::n, setzt X::x auf 1, nicht 2
Enumeratordeklaration
Für einen Namen, der im Initialisierungsteil der Enumerator-Deklaration verwendet wird, werden zuerst die zuvor deklarierten Enumeratoren in derselben Enumeration gefunden, bevor die unqualifizierte Namenssuche fortfährt, um den umschließenden Block, Klassen- oder Namensraum-Bereich zu untersuchen.
const int RED = 7; enum class color { RED, GREEN = RED + 2, // RED findet color::RED, nicht ::RED, also GREEN = 2 BLUE = ::RED + 4 // qualifizierte Suche findet ::RED, BLUE = 11 };
Handler eines Funktions- try -Blocks
Für einen Namen, der im Handler eines Funktion- try Blocks verwendet wird, erfolgt die Namenssuche so, als ob der Name ganz am Anfang des äußersten Blocks des Funktionsrumpfs verwendet würde (insbesondere sind Funktionsparameter sichtbar, aber Namen, die in diesem äußersten Block deklariert wurden, sind nicht sichtbar)
int n = 3; // gefunden 3. int f(int n = 2) // gefunden 2. try { int n = -1; // nie gefunden } catch(...) { // int n = 1; // gefunden 1. assert(n == 2); // Suche nach n findet Funktionsparameter f throw; }
Überladener Operator
Für einen Operator , der in einem Ausdruck verwendet wird (z.B. operator + in a + b ), gelten leicht unterschiedliche Lookup-Regeln gegenüber dem Operator in einem expliziten Funktionsaufruf wie operator + ( a, b ) : Beim Parsen eines Ausdrucks werden zwei separate Lookups durchgeführt: für Nicht-Member-Operator-Überladungen und für Member-Operator-Überladungen (für Operatoren, bei denen beide Formen erlaubt sind). Diese Mengen werden dann gleichberechtigt mit den integrierten Operator-Überladungen zusammengeführt, wie in Overload Resolution beschrieben. Bei Verwendung der expliziten Funktionsaufruf-Syntax wird ein regulärer unqualifizierter Name-Lookup durchgeführt:
struct A {}; void operator+(A, A); // benutzerdefinierter Nicht-Member-operator+ struct B { void operator+(B); // benutzerdefinierter Member-operator+ void f(); }; A a; void B::f() // Definition einer Member-Funktion von B { operator+(a, a); // Fehler: Reguläre Namenssuche aus einer Member-Funktion // findet die Deklaration von operator+ im Gültigkeitsbereich von B // und stoppt dort, ohne den globalen Gültigkeitsbereich zu erreichen a + a; // OK: Member-Suche findet B::operator+, Nicht-Member-Suche // findet ::operator+(A, A), Überladungsauflösung wählt ::operator+(A, A) }
Template-Definition
Für einen nicht-abhängigen Namen , der in einer Templatedefinition verwendet wird, erfolgt die unqualifizierte Namenssuche, wenn die Templatedefinition geprüft wird. Die Bindung an die Deklarationen, die zu diesem Zeitpunkt vorgenommen wurden, wird nicht durch Deklarationen beeinflusst, die zum Zeitpunkt der Instanziierung sichtbar sind. Für einen abhängigen Namen , der in einer Templatedefinition verwendet wird, wird die Suche verschoben, bis die Templateargumente bekannt sind. Zu diesem Zeitpunkt untersucht ADL Funktionsdeklarationen mit externer Bindung (bis C++11) , die sowohl vom Templatedefinitionskontext als auch vom Templateinstanziierungskontext sichtbar sind, während die Nicht-ADL-Suche nur Funktionsdeklarationen mit externer Bindung (bis C++11) untersucht, die vom Templatedefinitionskontext sichtbar sind (mit anderen Worten: Das Hinzufügen einer neuen Funktionsdeklaration nach der Templatedefinition macht sie nicht sichtbar, außer über ADL). Das Verhalten ist undefiniert, wenn es in den durch die ADL-Suche untersuchten Namensräumen eine bessere Übereinstimmung mit externer Bindung gibt, die in einer anderen Übersetzungseinheit deklariert ist, oder wenn die Suche mehrdeutig gewesen wäre, wenn diese Übersetzungseinheiten geprüft worden wären. In jedem Fall wird, wenn eine Basisklasse von einem Templateparameter abhängt, ihr Gültigkeitsbereich nicht durch die unqualifizierte Namenssuche untersucht (weder zum Zeitpunkt der Definition noch zum Zeitpunkt der Instanziierung).
void f(char); // erste Deklaration von f template<class T> void g(T t) { f(1); // nicht-abhängiger Name: Suche findet ::f(char) und bindet es jetzt f(T(1)); // abhängiger Name: Suche wird verschoben f(t); // abhängiger Name: Suche wird verschoben // dd++; // nicht-abhängiger Name: Suche findet keine Deklaration } enum E { e }; void f(E); // zweite Deklaration von f void f(int); // dritte Deklaration von f double dd; void h() { g(e); // instanziiert g<E>, zu diesem Zeitpunkt // werden die zweite und dritte Verwendung des Namens 'f' // gesucht und finden ::f(char) (durch Namenssuche) und ::f(E) (durch ADL) // dann wählt die Überladungsauflösung ::f(E). // Dies ruft f(char), dann f(E) zweimal auf g(32); // instanziiert g<int>, zu diesem Zeitpunkt // werden die zweite und dritte Verwendung des Namens 'f' // gesucht und finden nur ::f(char) // dann wählt die Überladungsauflösung ::f(char) // Dies ruft f(char) dreimal auf } typedef double A; template<class T> class B { typedef int A; }; template<class T> struct X : B<T> { A a; // Suche nach A findet ::A (double), nicht B<T>::A };
Hinweis: siehe dependent name lookup rules für die Begründung und Implikationen dieser Regel.
Vorlagenname
|
Dieser Abschnitt ist unvollständig
Grund: Dual-Scope-Lookup des Template-Namens nach -> und . |
Element einer Klassenvorlage außerhalb der Vorlage
| Dieser Abschnitt ist unvollständig |
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 490 | C++98 |
jeder Name in einem Template-Argument in einer friend
Member-Funktionsdeklaration wurde nicht im Gültigkeitsbereich der Member-Funktionsklasse gesucht |
schließt nur die Namen
in Template-Argumenten im Deklarator-Identifier aus |
| CWG 514 | C++98 |
jeder unqualifizierte Name, der im Namespace-
Gültigkeitsbereich verwendet wurde, wurde zuerst in diesem Gültigkeitsbereich gesucht |
die unqualifizierten Namen, die zur Definition eines
Namespace-Variablen-Members außerhalb dieses Namespace verwendet werden, werden zuerst in diesem Namespace gesucht |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
- 6.5 Namenssuche [basic.lookup] (S: 44-45)
-
- 6.5.2 Mitgliedsnamenssuche [class.member.lookup] (S: 45-47)
-
- 13.8 Namensauflösung [temp.res] (S: 399-403)
- C++20-Standard (ISO/IEC 14882:2020):
-
- 6.5 Namenssuche [basic.lookup] (S: 38-50)
-
- 11.8 Elementnamenssuche [class.member.lookup] (S: 283-285)
-
- 13.8 Namensauflösung [temp.res] (S: 385-400)
- C++17-Standard (ISO/IEC 14882:2017):
-
- 6.4 Namenssuche [basic.lookup] (S: 50-63)
-
- 13.2 Mitgliedsnamenssuche [class.member.lookup] (S: 259-262)
-
- 17.6 Namensauflösung [temp.res] (S: 375-378)
- C++14-Standard (ISO/IEC 14882:2014):
-
- 3.4 Namenssuche [basic.lookup] (S: 42-56)
-
- 10.2 Mitgliedsnamenssuche [class.member.lookup] (S: 233-236)
-
- 14.6 Namensauflösung [temp.res] (S: 346-359)
- C++11-Standard (ISO/IEC 14882:2011):
-
- 3.4 Namenssuche [basic.lookup]
-
- 10.2 Mitgliedsnamenssuche [class.member.lookup]
-
- 14.6 Namensauflösung [temp.res]
- C++98 Standard (ISO/IEC 14882:1998):
-
- 3.4 Namenssuche [basic.lookup]
-
- 10.2 Mitgliedsnamenssuche [class.member.lookup]
-
- 14.6 Namensauflösung [temp.res]