Qualified name lookup
Ein
qualifizierter
Name ist ein Name, der auf der rechten Seite des Bereichsauflösungsoperators
::
erscheint (siehe auch
qualifizierte Bezeichner
).
Ein qualifizierter Name kann sich auf ein
- Klassenmitglied (einschließlich statischer und nicht-statischer Funktionen, Typen, Templates usw.),
- Namespace-Mitglied (einschließlich eines anderen Namespace),
- Enumerator.
Wenn sich auf der linken Seite des
::
nichts befindet, berücksichtigt die Suche nur Deklarationen im
globalen Namensbereich
. Dies ermöglicht es, auf solche Namen zu verweisen, 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; // Fehler: unqualifizierte Suche nach 'std' findet die Struktur ::std::cout << ::ok; // OK: ::std findet den Namespace std }
Bevor die Namenssuche für den Namen auf der rechten Seite von
::
durchgeführt werden kann, muss die Suche für den Namen auf der linken Seite abgeschlossen sein (es sei denn, ein
decltype
-Ausdruck wird verwendet oder es befindet sich nichts auf der linken Seite). Diese Suche, die qualifiziert oder unqualifiziert sein kann, abhängig davon, ob sich ein weiteres
::
links von diesem Namen befindet, berücksichtigt nur Namespaces, Klassentypen, Enumerationen und Templates, deren Spezialisierungen Typen sind. Wenn der auf der linken Seite gefundene Name keinen Namespace oder keinen Klassen-, Enumerations- oder abhängigen Typ bezeichnet, ist das Programm fehlerhaft:
struct A { static int n; }; int main() { int A; A::n = 42; // OK: Unqualifizierte Suche nach A links von :: ignoriert die Variable A b; // Fehler: Unqualifizierte Suche nach A findet die Variable A } template<int> struct B : A {}; namespace N { template<int> void B(); int f() { return B<0>::n; // Fehler: N::B<0> ist kein Typ } }
Wenn ein qualifizierter Name als Deklarator verwendet wird, dann wird unqualified lookup der Namen, die im selben Deklarator nach diesem qualifizierten Namen verwendet werden, jedoch nicht der Namen, die ihm vorausgehen, im Gültigkeitsbereich der Klasse oder des Namespace des Members 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]; // Fehler: Suche nach X findet ::X, nicht C::X C::X C::arr[number], brr[number]; // OK: Größe von arr ist 50, Größe von brr ist 100
Wenn
::
vom Zeichen
~
gefolgt wird, das seinerseits von einem Bezeichner gefolgt wird (d.h., es spezifiziert einen Destruktor oder Pseudo-Destruktor), wird dieser Bezeichner im selben Gültigkeitsbereich 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(); // Der Name I nach ~ wird im selben Gültigkeitsbereich gesucht wie I vor :: // (also innerhalb des Gültigkeitsbereichs von C, daher findet es C::I) q->I1::~I2(); // Der Name I2 wird im selben Gültigkeitsbereich gesucht wie I1 // (also vom aktuellen Gültigkeitsbereich aus, daher findet es ::I2) AB x; x.AB::~AB(); // Der Name AB nach ~ wird im selben Gültigkeitsbereich gesucht wie AB vor :: // (also vom aktuellen Gültigkeitsbereich aus, daher findet es ::AB) }
EnumeratorenWenn die Suche nach dem Namen auf der linken Seite zu einer Enumeration (entweder scoped oder unscoped) führt, muss die Suche nach der rechten Seite zu einem Enumerator führen, der zu dieser Enumeration gehört, andernfalls ist das Programm fehlerhaft. |
(seit C++11) |
Klassenmitglieder
Wenn die Suche nach dem Namen auf der linken Seite einen Klassen-/Struktur- oder Union-Namen ergibt, wird der Name auf der rechten Seite von
::
im Gültigkeitsbereich dieser Klasse gesucht (und kann somit eine Deklaration eines Members dieser Klasse oder ihrer Basisklassen finden), mit folgenden Ausnahmen:
- Ein Destruktor wird wie oben beschrieben gesucht (im Gültigkeitsbereich des Namens links von ::).
- Eine conversion-type-id in einem user-defined conversion Funktionsnamen wird zuerst im Gültigkeitsbereich der Klasse gesucht. Wenn nicht gefunden, wird der Name dann im aktuellen Gültigkeitsbereich gesucht.
- Namen, die in Template-Argumenten verwendet werden, werden im aktuellen Gültigkeitsbereich gesucht (nicht im Gültigkeitsbereich des Template-Namens).
- Namen in using-declarations berücksichtigen auch Klassen-/Enum-Namen, die durch den Namen einer Variable, Datenelements, Funktion oder Enumerators verdeckt werden, die im selben Gültigkeitsbereich deklariert sind.
|
Dieser Abschnitt ist unvollständig
Grund: Mikro-Beispiele für die obigen Punkte |
Wenn die rechte Seite von
::
dieselbe Klasse benennt wie die linke Seite, 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
vererbten Konstruktor
verwendet werden. In jenen Namenssuchen, bei denen Funktionsnamen ignoriert werden (d.h. bei der Suche nach einem Namen links von
::
, bei der Suche nach einem Namen in einem
elaborated type specifier
oder einem
base specifier
), löst dieselbe Syntax den injizierten Klassennamen auf:
struct A { A(); }; struct B : A { B(); }; A::A() {} // A::A benennt einen Konstruktor, verwendet in einer Deklaration B::B() {} // B::B benennt einen Konstruktor, verwendet in einer Deklaration B::A ba; // B::A benennt den Typ A (gesucht im Gültigkeitsbereich von B) A::A a; // Fehler: A::A benennt keinen Typ struct A::A a2; // OK: Suche in elaborated type specifier ignoriert Funktionen // also benennt A::A einfach die Klasse A, wie sie aus dem Gültigkeitsbereich von A sichtbar ist // (das heißt, den injizierten Klassenname)
Qualifizierte Namenssuche kann verwendet werden, um auf ein Klassenmitglied zuzugreifen, das durch eine geschachtelte Deklaration oder durch eine abgeleitete Klasse verborgen ist. Ein Aufruf einer qualifizierten Memberfunktion ist niemals virtuell:
struct B { virtual void foo(); }; struct D : B { void foo() override; }; int main() { D x; B& b = x; b.foo(); // Ruft D::foo auf (virtueller Aufruf) b.B::foo(); // Ruft B::foo auf (statischer Aufruf) }
Namespace-Mitglieder
Wenn der Name auf der linken Seite von
::
auf einen Namespace verweist oder wenn nichts auf der linken Seite von
::
steht (in diesem Fall verweist es auf den globalen Namespace), wird der Name, der auf der rechten Seite von
::
erscheint, im Gültigkeitsbereich dieses Namespaces gesucht, außer dass
- Namen, die in Template-Argumenten verwendet werden, werden im aktuellen Gültigkeitsbereich gesucht:
namespace N { template<typename T> struct foo {}; struct X {}; } N::foo<X> x; // Fehler: X wird als ::X gesucht, nicht als N::X
Qualifizierte Suche innerhalb des Geltungsbereichs eines
Namespace
N
betrachtet zunächst 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 in dieser Menge keine Deklarationen gibt, dann betrachtet sie Deklarationen in allen Namespaces, die durch
using-Direktiven
in
N
und in allen transitiven Inline-Namespace-Mitgliedern von
N
angegeben sind. 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 wird durchsucht, AB::g wird durch Lookup gefunden und ausgewählt AB::g(void) // (A und B werden nicht durchsucht) AB::f(1); // Zuerst wird AB durchsucht. Es gibt kein f // Dann werden A, B durchsucht // A::f, B::f werden durch Lookup gefunden // (aber Y wird nicht durchsucht, daher wird Y::f nicht berücksichtigt) // Überladungsauflösung wählt A::f(int) AB::x++; // Zuerst wird AB durchsucht. Es gibt kein x // Dann werden A, B durchsucht. Es gibt kein x // Dann werden Y und Z durchsucht. Es gibt immer noch kein x: Dies ist ein Fehler AB::i++; // AB wird durchsucht. Es gibt kein i // Dann werden A, B durchsucht. A::i und B::i werden durch Lookup gefunden: Dies ist ein Fehler AB::h(16.8); // Zuerst wird AB durchsucht. Es gibt kein h // Dann werden A, B durchsucht. Es gibt kein h // Dann werden Y und Z durchsucht // Lookup findet Y::h und Z::h. Überladungsauflösung wählt Z::h(double) }
Es ist zulässig, dass dieselbe Deklaration mehr als einmal 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: findet dasselbe A::a durch B und durch D }
|
Dieser Abschnitt ist unvollständig
Grund: der Rest von N4861 6.5.3.2[namespace.qual], versuchen Sie deren Beispiele zu kürzen |
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 215 | C++98 |
der Name vor
::
musste ein Klassenname oder Namespace-Name sein,
daher waren Template-Parameter dort nicht erlaubt |
der Name muss eine Klasse,
einen Namespace oder einen abhängigen Typ bezeichnen |
| CWG 318 | C++98 |
wenn die rechte Seite von
::
dieselbe Klasse benennt
wie die linke Seite, wurde der qualifizierte Name immer als Benennung des Konstruktors dieser Klasse betrachtet |
benennt nur den Konstruktor,
wenn akzeptabel (z.B. nicht in einem elaborated type specifier) |