Argument-dependent lookup
Argument-dependent lookup (ADL), auch bekannt als Koenig lookup [1] , ist der Satz von Regeln für die Suche nach unqualifizierten Funktionsnamen in Funktionsaufrufausdrücken , einschließlich impliziter Funktionsaufrufe an überladene Operatoren . Diese Funktionsnamen werden zusätzlich zu den Geltungsbereichen und Namensräumen, die durch die übliche unqualifizierte Namenssuche berücksichtigt werden, in den Namensräumen ihrer Argumente gesucht.
Argument-dependent lookup ermöglicht die Verwendung von Operatoren, die in einem anderen Namespace definiert sind. Beispiel:
#include <iostream> int main() { std::cout << "Test\n"; // There is no operator<< in global namespace, but ADL // examines std namespace because the left argument is in // std and finds std::operator<<(std::ostream&, const char*) operator<<(std::cout, "Test\n"); // Same, using function call notation // However, std::cout << endl; // Error: “endl” is not declared in this namespace. // This is not a function call to endl(), so ADL does not apply endl(std::cout); // OK: this is a function call: ADL examines std namespace // because the argument of endl is in std, and finds std::endl (endl)(std::cout); // Error: “endl” is not declared in this namespace. // The sub-expression (endl) is not an unqualified-id }
Inhaltsverzeichnis |
Details
Zunächst wird das argumentabhängige Lookup nicht berücksichtigt, falls die durch das übliche unqualified lookup erzeugte Lookup-Menge eines der folgenden Elemente enthält:
Andernfalls wird für jedes Argument in einem Funktionsaufrufausdruck dessen Typ untersucht, um den zugehörigen Satz von Namensräumen und Klassen zu bestimmen, der zur Suche hinzugefügt wird.
T
oder Zeiger auf ein Array von
T
wird der Typ
T
untersucht und sein zugehöriger Satz von Klassen und Namensräumen zum Satz hinzugefügt.
F
der Klasse
X
werden die Funktionsparametertypen, der Funktionstyp und die Klasse
X
untersucht und ihre zugehörigen Mengen von Klassen und Namensräumen zur Menge hinzugefügt.
T
der Klasse
X
werden sowohl der Member-Typ als auch der Typ
X
untersucht und ihre zugehörigen Mengen von Klassen und Namensräumen zur Menge hinzugefügt.
- Zusätzlich, wenn die Überladungsmenge durch einen Template-Bezeichner benannt wird, werden alle seine Typ-Template-Argumente und Template-Template-Argumente (aber keine konstanten Template-Argumente) untersucht und ihre zugehörigen Sätze von Klassen und Namensräumen werden zum Satz hinzugefügt.
|
Wenn ein beliebiger Namespace in der zugehörigen Menge von Klassen und Namespaces ein Inline-Namespace ist, wird sein umschließender Namespace ebenfalls zur Menge hinzugefügt. Wenn ein beliebiger Namespace in der zugehörigen Menge von Klassen und Namespaces direkt einen Inline-Namespace enthält, wird dieser Inline-Namespace zur Menge hinzugefügt. |
(seit C++11) |
Nachdem der zugehörige Satz von Klassen und Namensräumen bestimmt wurde, werden alle Deklarationen, die in Klassen dieses Satzes gefunden werden, für den weiteren ADL-Prozess verworfen, mit Ausnahme von namespace-spezifischen Friend-Funktionen und Funktionsvorlagen, wie in Punkt 2 unten angegeben.
Die Menge der Deklarationen, die durch gewöhnliche unqualified lookup gefunden werden, und die Menge der Deklarationen, die in allen Elementen des durch ADL erzeugten assoziierten Sets gefunden werden, werden mit folgenden speziellen Regeln zusammengeführt:
Hinweise
Aufgrund des argumentabhängigen Namenslookups werden nicht-Member-Funktionen und nicht-Member-Operatoren, die im selben Namespace wie eine Klasse definiert sind, als Teil der öffentlichen Schnittstelle dieser Klasse betrachtet (wenn sie durch ADL gefunden werden) [2] .
ADL ist der Grund für die etablierte Idiom zum Austauschen zweier Objekte in generischem Code:
using
std::
swap
;
swap
(
obj1, obj2
)
;
weil der direkte Aufruf von
std::
swap
(
obj1, obj2
)
keine benutzerdefinierten
swap()
Funktionen berücksichtigen würde, die im selben Namespace wie die Typen von
obj1
oder
obj2
definiert sein könnten, und der unqualifizierte Aufruf
swap
(
obj1, obj2
)
ohne benutzerdefinierte Überladung nichts aufrufen würde. Insbesondere verwenden
std::iter_swap
und alle anderen Standardbibliothek-Algorithmen diesen Ansatz beim Umgang mit
Swappable
Typen.
Die Namenssucheregeln machen es unpraktisch, Operatoren im globalen oder benutzerdefinierten Namespace zu deklarieren, die auf Typen aus dem
std
-Namespace operieren, z.B. ein benutzerdefinierter
operator
>>
oder
operator
+
für
std::vector
oder für
std::pair
(es sei denn, die Elementtypen des Vectors/Paars sind benutzerdefinierte Typen, die ihren eigenen Namespace zur ADL hinzufügen würden). Solche Operatoren würden nicht von Template-Instanziierungen aus gesucht, wie z.B. den Standardbibliothek-Algorithmen. Siehe
abhängige Namen
für weitere Details.
ADL kann eine friend-Funktion (typischerweise einen überladenen Operator) finden, die vollständig innerhalb einer Klasse oder Klassenvorlage definiert ist, selbst wenn sie niemals auf Namespace-Ebene deklariert wurde.
template<typename T> struct number { number(int); friend number gcd(number x, number y) { return 0; }; // Definition innerhalb // einer Klassenvorlage }; // Sofern keine passende Deklaration bereitgestellt wird, ist gcd // ein unsichtbares (außer durch ADL) Mitglied dieses Namensraums void g() { number<double> a(3), b(4); a = gcd(a, b); // Findet gcd, weil number<double> eine assoziierte Klasse ist, // wodurch gcd in seinem Namensraum (globaler Gültigkeitsbereich) sichtbar wird // b = gcd(3, 4); // Fehler; gcd ist nicht sichtbar }
|
Obwohl ein Funktionsaufruf durch ADL aufgelöst werden kann, selbst wenn die gewöhnliche Suche nichts findet, erfordert ein Funktionsaufruf an eine Funktionsvorlage mit explizit angegebenen Template-Argumenten, dass eine Deklaration der Vorlage durch gewöhnliche Suche gefunden wird (andernfalls ist es ein Syntaxfehler, auf einen unbekannten Namen gefolgt von einem Kleiner-als-Zeichen zu stoßen). namespace N1 { struct S {}; template<int X> void f(S); } namespace N2 { template<class T> void f(T t); } void g(N1::S s) { f<3>(s); // Syntax error until C++20 (unqualified lookup finds no f) N1::f<3>(s); // OK, qualified lookup finds the template 'f' N2::f<3>(s); // Error: N2::f does not take a constant parameter // N1::f is not looked up because ADL only works // with unqualified names using N2::f; f<3>(s); // OK: Unqualified lookup now finds N2::f // then ADL kicks in because this name is unqualified // and finds N1::f } |
(bis C++20) |
In den folgenden Kontexten findet ausschließlich ADL-Lookup statt (d.h. Suche nur in assoziierten Namensräumen):
|
(since C++11) |
- die dependent name lookup vom Punkt der Template-Instanziierung aus.
|
(since C++17) |
Beispiele
|
Dieser Abschnitt ist unvollständig
Grund: Weitere Beispiele benötigt |
Beispiel von http://www.gotw.ca/gotw/030.htm
namespace A { struct X; struct Y; void f(int); void g(X); } namespace B { void f(int i) { f(i); // Ruft B::f auf (Endlosschleife) } void g(A::X x) { g(x); // Fehler: Mehrdeutig zwischen B::g (gewöhnliche Suche) // und A::g (argumentabhängige Suche) } void h(A::Y y) { h(y); // Ruft B::h auf (Endlosschleife): ADL untersucht den A-Namensraum // findet aber kein A::h, daher wird nur B::h aus gewöhnlicher Suche verwendet } }
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 33 | C++98 |
Die zugeordneten Namensräume oder Klassen sind nicht spezifiziert
wenn ein für die Suche verwendetes Argument die Adresse einer Gruppe überladener Funktionen oder einer Funktionsvorlage ist |
spezifiziert |
| CWG 90 | C++98 |
Die zugeordneten Klassen einer geschachtelten Nicht-Union-Klasse
enthielten nicht ihre einschließende Klasse, aber eine geschachtelte Union war ihrer einschließenden Klasse zugeordnet |
Auch Nicht-Unions zugeordnet |
| CWG 239 | C++98 |
Eine Blockbereich-Funktionsdeklaration, die in der gewöhnlichen
unqualifizierten Suche gefunden wurde, verhinderte nicht das Auftreten von ADL |
ADL nicht berücksichtigt außer
für using Deklarationen |
| CWG 997 | C++98 |
Abhängige Parametertypen und Rückgabetypen waren
von der Berücksichtigung bei der Bestimmung der zugeordneten Klassen und Namensräume einer Funktionsvorlage ausgeschlossen |
eingeschlossen |
| CWG 1690 |
C++98
C++11 |
ADL konnte Lambdas (C++11) oder Objekte
von lokalen Klassentypen (C++98), die zurückgegeben werden, nicht finden |
Sie können gefunden werden |
| CWG 1691 | C++11 | ADL zeigte überraschende Verhaltensweisen für opaque Enum-Deklarationen | behoben |
| CWG 1692 | C++98 |
Doppelt geschachtelte Klassen hatten keine zugeordneten Namensräume
(ihre einschließenden Klassen sind keine Mitglieder eines Namensraums) |
Zugeordnete Namensräume sind
auf die innersten einschließenden Namensräume erweitert |
| CWG 2857 | C++98 |
Die zugeordneten Klassen eines unvollständigen
Klassentyps schlossen seine Basisklassen ein |
Nicht eingeschlossen |
Siehe auch
Externe Links
|