Qualifizierte Namenssuche
Ein qualifizierter Name ist ein Name, der auf der rechten Seite des Scope-Resolution-Operators :: erscheint (siehe auch qualifizierte Bezeichner). Ein qualifizierter Name kann sich auf ein
- Klassenmitglied (einschließlich statischer und nicht-statischer Funktionen, Typen, Templates usw.),
- Namespacemmitglied (einschließlich eines anderen Namespaces),
- Enumerator beziehen.
Wenn sich nichts auf der linken Seite von :: befindet, berücksichtigt die Suche nur Deklarationen im globalen Namespace-Scope. Dies ermöglicht es, solche Namen zu referenzieren, selbst wenn sie durch eine lokale Deklaration verborgen wurden.
#include <iostream> namespace M { const char* fail = "fail\n"; } using M::fail; namespace N { const char* ok = "ok\n"; } using namespace N; int main() { struct std {}; std::cout << ::fail; // Error: unqualified lookup for 'std' finds the struct ::std::cout << ::ok; // OK: ::std finds the namespace std }
Bevor eine Namenssuche für den Namen auf der rechten Seite von :: durchgeführt werden kann, muss die Suche für den Namen auf seiner linken Seite abgeschlossen sein (es sei denn, ein decltype-Ausdruck wird verwendet oder auf der linken Seite befindet sich nichts). Diese Suche, die je nachdem, ob sich weiter links ein weiterer :: befindet, qualifiziert oder unqualifiziert sein kann, berücksichtigt nur Namespaces, Klassentypen, Aufzählungen und Templates, deren Spezialisierungen Typen sind. Wenn der auf der linken Seite gefundene Name keinen Namespace oder keine Klasse, Aufzählung oder keinen abhängigen Typ bezeichnet, ist das Programm fehlerhaft.
struct A { static int n; }; int main() { int A; A::n = 42; // OK: unqualified lookup of A to the left of :: ignores the variable A b; // Error: unqualified lookup of A finds the variable A } template<int> struct B : A {}; namespace N { template<int> void B(); int f() { return B<0>::n; // Error: N::B<0> is not a type } }
Wenn ein qualifizierter Name als Deklarator verwendet wird, dann wird die unqualifizierte Suche für die Namen, die im selben Deklarator nach diesem qualifizierten Namen verwendet werden, aber nicht für die Namen, die ihm vorausgehen, im Scope der Klasse oder des Namespaces des Mitglieds durchgeführt.
class X {}; constexpr int number = 100; struct C { class X {}; static const int number = 50; static X arr[number]; }; X C::arr[number], brr[number]; // Error: look up for X finds ::X, not C::X C::X C::arr[number], brr[number]; // OK: size of arr is 50, size of brr is 100
Wenn :: auf das Zeichen ~ folgt, dem wiederum ein Bezeichner folgt (d. h. es wird ein Destruktor oder Pseudo-Destruktor angegeben), wird dieser Bezeichner im selben Scope wie der Name auf der linken Seite von :: gesucht.
struct C { typedef int I; }; typedef int I1, I2; extern int *p, *q; struct A { ~A(); }; typedef A AB; int main() { p->C::I::~I(); // The name I after ~ is looked up in the same scope as I before :: // (that is, within the scope of C, so it finds C::I) q->I1::~I2(); // The name I2 is looked up in the same scope as I1 // (that is, from the current scope, so it finds ::I2) AB x; x.AB::~AB(); // The name AB after ~ is looked up in the same scope as AB before :: // (that is, from the current scope, so it finds ::AB) }
EnumeratorenWenn die Suche des Namens auf der linken Seite eine Aufzählung (entweder mit oder ohne Scope) ergibt, muss die Suche des Namens auf der rechten Seite einen Enumerator ergeben, der zu dieser Aufzählung gehört, andernfalls ist das Programm fehlerhaft. |
(seit C++11) |
[edit] Klassenmitglieder
Wenn die Suche des Namens auf der linken Seite einen Klassen-/Struktur- oder Union-Namen ergibt, wird der Name auf der rechten Seite von :: im Scope dieser Klasse gesucht (und kann somit eine Deklaration eines Mitglieds dieser Klasse oder ihrer Basis finden), mit folgenden Ausnahmen:
- Ein Destruktor wird wie oben beschrieben gesucht (im Scope des Namens links von ::).
- Ein conversion-type-id in einem benutzerdefinierten Konvertierungsoperator wird zuerst im Scope der Klasse gesucht. Wenn nicht gefunden, wird der Name dann im aktuellen Scope gesucht.
- Namen, die in Template-Argumenten verwendet werden, werden im aktuellen Scope gesucht (nicht im Scope des Template-Namens).
- Namen in using-Deklarationen berücksichtigen auch Klassen-/Enum-Namen, die durch den Namen einer Variable, eines Datenmitglieds, einer Funktion oder eines Enumerators im selben Scope verborgen werden.
| Dieser Abschnitt ist unvollständig Grund: Mikro-Beispiele für oben |
Wenn die rechte Seite von :: dieselbe Klasse wie die linke Seite benennt, bezeichnet der Name den Konstruktor dieser Klasse. Ein solcher qualifizierter Name kann nur in einer Deklaration eines Konstruktors und in der using-Deklaration für einen erbenden Konstruktor verwendet werden. Bei Suchen, bei denen Funktionsnamen ignoriert werden (d. h. beim Suchen eines Namens links von ::, beim Suchen eines Namens in einem elaborierten Typspezifizierer oder Basis-Spezifizierer), löst dieselbe Syntax zum injected-class-name auf.
struct A { A(); }; struct B : A { B(); }; A::A() {} // A::A names a constructor, used in a declaration B::B() {} // B::B names a constructor, used in a declaration B::A ba; // B::A names the type A (looked up in the scope of B) A::A a; // Error: A::A does not name a type struct A::A a2; // OK: lookup in elaborated type specifier ignores functions // so A::A simply names the class A as seen from within the scope of A // (that is, the injected-class-name)
Qualifizierte Namenssuche kann verwendet werden, um auf ein Klassenmitglied zuzugreifen, das durch eine verschachtelte Deklaration oder durch eine abgeleitete Klasse verborgen ist. Ein Aufruf einer qualifizierten Member-Funktion ist niemals virtuell.
struct B { virtual void foo(); }; struct D : B { void foo() override; }; int main() { D x; B& b = x; b.foo(); // Calls D::foo (virtual dispatch) b.B::foo(); // Calls B::foo (static dispatch) }
[edit] Namespacemmitglieder
Wenn sich der Name auf der linken Seite von :: auf einen Namespace bezieht oder wenn sich nichts auf der linken Seite von :: befindet (in diesem Fall bezieht er sich auf den globalen Namespace), wird der Name, der auf der rechten Seite von :: erscheint, im Scope dieses Namespaces gesucht, außer dass
- Namen, die in Template-Argumenten verwendet werden, im aktuellen Scope gesucht werden.
namespace N { template<typename T> struct foo {}; struct X {}; } N::foo<X> x; // Error: X is looked up as ::X, not as N::X
Qualifizierte Suche innerhalb des Scopes eines Namespaces N berücksichtigt zuerst alle Deklarationen, die sich in N befinden, und alle Deklarationen, die sich in den Inline-Namespace-Mitgliedern von N befinden (und transitiv in deren Inline-Namespace-Mitgliedern). Wenn es keine Deklarationen in dieser Menge gibt, dann berücksichtigt sie Deklarationen in allen Namespaces, die durch using-Direktiven in N und in allen transitiven Inline-Namespace-Mitgliedern von N benannt werden. Die Regeln werden rekursiv angewendet.
int x; namespace Y { void f(float); void h(int); } namespace Z { void h(double); } namespace A { using namespace Y; void f(int); void g(int); int i; } namespace B { using namespace Z; void f(char); int i; } namespace AB { using namespace A; using namespace B; void g(); } void h() { AB::g(); // AB is searched, AB::g found by lookup and is chosen AB::g(void) // (A and B are not searched) AB::f(1); // First, AB is searched. There is no f // Then, A, B are searched // A::f, B::f found by lookup // (but Y is not searched so Y::f is not considered) // Overload resolution picks A::f(int) AB::x++; // First, AB is searched. There is no x // Then A, B are searched. There is no x // Then Y and Z are searched. There is still no x: this is an error AB::i++; // AB is searched. There is no i // Then A, B are searched. A::i and B::i found by lookup: this is an error AB::h(16.8); // First, AB is searched. There is no h // Then A, B are searched. There is no h // Then Y and Z are searched // Lookup finds Y::h and Z::h. Overload resolution picks Z::h(double) }
Es ist zulässig, dass dieselbe Deklaration mehrmals gefunden wird.
namespace A { int a; } namespace B { using namespace A; } namespace D { using A::a; } namespace BD { using namespace B; using namespace D; } void g() { BD::a++; // OK: finds the same A::a through B and through D }
| Dieser Abschnitt ist unvollständig Grund: der Rest von N4861 6.5.3.2[namespace.qual], versuchen, deren Beispiele zu kürzen |
[edit] 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 215 | C++98 | der Name, der :: vorausgeht, muss ein Klassenname oder Namespace seinName, daher waren Template-Parameter dort nicht erlaubt |
der Name muss eine Klasse bezeichnen, Namespace oder abhängigen Typ |
| CWG 318 | C++98 | wenn die rechte Seite von :: dieselbe Klasse benenntwie die linke Seite, wurde der qualifizierte Name immer als Benennung des Konstruktors dieser Klasse betrachtet |
nur den Konstruktor benennen wenn akzeptabel (z. B. nicht in einem elaborierten Typspezifizierer) |