Argumentabhängige Namensauflösung
Argument-dependent lookup (ADL), auch bekannt als Koenig-Lookup[1], ist die Menge von Regeln für die Suche nach nicht qualifizierten Funktionsnamen in Funktionsaufrufen, einschließlich impliziter Funktionsaufrufe an überladene Operatoren. Diese Funktionsnamen werden in den Namespaces ihrer Argumente zusätzlich zu den Bereichen und Namespaces gesucht, die durch die übliche nicht qualifizierte Namenssuche berücksichtigt werden.
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 }
Inhalt |
[bearbeiten] Details
Zuerst wird die argumentabhängige Suche nicht berücksichtigt, wenn die durch die übliche nicht qualifizierte Suche erzeugte Menge von Suchen eine der folgenden enthält:
Andernfalls wird für jedes Argument in einem Funktionsaufruf dessen Typ untersucht, um die *zugehörigen Mengen von Namespaces und Klassen* zu ermitteln, die der Suche hinzugefügt werden.
T oder Zeiger auf ein Array von T wird der Typ T untersucht und seine zugehörige Menge von Klassen und Namespaces zur Menge hinzugefügt.F einer Klasse X werden die Parametertypen der Funktion, der Rückgabetyp der Funktion und die Klasse X untersucht und ihre zugehörige Menge von Klassen und Namespaces zur Menge hinzugefügt.T einer Klasse X werden der Elementtyp und der Typ X beide untersucht und ihre zugehörige Menge von Klassen und Namespaces zur Menge hinzugefügt.- Zusätzlich, wenn die Menge von Überladungen durch einen Template-Identifier benannt wird, werden alle seine Typ-Template-Argumente und Template-Template-Argumente (aber keine Nicht-Typ-Template-Argumente) untersucht und ihre zugehörige Menge von Klassen und Namespaces zur Menge hinzugefügt.
|
Wenn ein 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 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 die zugehörige Menge von Klassen und Namespaces bestimmt wurde, werden alle Deklarationen, die in Klassen dieser Menge gefunden wurden, für die weitere ADL-Verarbeitung verworfen, mit Ausnahme von Friend-Funktionen im Namespace-Scope und Funktionstemplate, wie in Punkt 2 unten angegeben.
Die Menge der Deklarationen, die durch die gewöhnliche nicht qualifizierte Suche gefunden werden, und die Menge der Deklarationen, die in allen Elementen der durch ADL erzeugten zugehörigen Menge gefunden werden, werden zusammengeführt, mit folgenden Sonderregeln:
[bearbeiten] Anmerkungen
Aufgrund von Argument-dependent lookup gelten Nicht-Member-Funktionen und Nicht-Member-Operatoren, die im selben Namespace wie eine Klasse definiert sind, als Teil der öffentlichen Schnittstelle dieser Klasse (wenn sie durch ADL gefunden werden)[2].
ADL ist der Grund für das etablierte Idiom zum Vertauschen zweier Objekte in generischem Code: using std::swap; swap(obj1, obj2); Da 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 ein direkter Aufruf des nicht qualifizierten swap(obj1, obj2) nichts aufrufen würde, wenn keine benutzerdefinierte Überladung bereitgestellt wurde. Insbesondere verwenden std::iter_swap und alle anderen Standardbibliotheksalgorithmen diesen Ansatz, wenn sie mit Swappable-Typen arbeiten.
Namenssuchregeln 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 Vektors/Paares sind benutzerdefinierte Typen, die ihren Namespace zu ADL hinzufügen würden). Solche Operatoren würden aus Template-Instanziierungen wie den Standardbibliotheksalgorithmen nicht gefunden werden. Weitere Details finden Sie unter abhängige Namen.
ADL kann eine Friend-Funktion (typischerweise ein überladener Operator) finden, die vollständig innerhalb einer Klasse oder eines Klassentemplates definiert ist, selbst wenn sie nie auf Namespace-Ebene deklariert wurde.
template<typename T> struct number { number(int); friend number gcd(number x, number y) { return 0; }; // Definition within // a class template }; // Unless a matching declaration is provided gcd is // an invisible (except through ADL) member of this namespace void g() { number<double> a(3), b(4); a = gcd(a, b); // Finds gcd because number<double> is an associated class, // making gcd visible in its namespace (global scope) // b = gcd(3, 4); // Error; gcd is not visible }
|
Obwohl ein Funktionsaufruf durch ADL aufgelöst werden kann, auch wenn die normale Suche nichts findet, erfordert ein Funktionsaufruf an ein Funktionstemplate mit explizit angegebenen Template-Argumenten, dass eine durch normale Suche gefundene Deklaration des Templates existiert (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 non-type 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 eine reine ADL-Suche (d. h. Suche nur in zugehörigen Namespaces) statt:
|
(seit C++11) |
- die Suche nach abhängigen Namen ab dem Zeitpunkt der Template-Instanziierung.
|
(seit C++17) |
[bearbeiten] Beispiele
| Dieser Abschnitt ist unvollständig Grund: weitere Beispiele |
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); // Calls B::f (endless recursion) } void g(A::X x) { g(x); // Error: ambiguous between B::g (ordinary lookup) // and A::g (argument-dependent lookup) } void h(A::Y y) { h(y); // Calls B::h (endless recursion): ADL examines the A namespace // but finds no A::h, so only B::h from ordinary lookup is used } }
[bearbeiten] 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 33 | C++98 | die zugehörigen Namespaces oder Klassen sind nicht spezifiziert wenn ein für die Suche verwendetes Argument die Adresse eines Gruppe von überladenen Funktionen oder eines Funktionstemplate ist |
spezifiziert |
| CWG 90 | C++98 | die zugehörigen Klassen einer verschachtelten Nicht-Union-Klasse schlossen ihre umschließende Klasse nicht ein, aber eine verschachtelte Union war mit ihrer umschließenden Klasse verbunden |
Nicht-Unions auch verbunden |
| CWG 239 | C++98 | eine Funktionsdeklaration auf Blockebene, die in der normalen nicht qualifizierten Suche gefunden wurde, verhinderte nicht, dass ADL stattfand |
ADL wurde nicht berücksichtigt, außer für using Deklarationen |
| CWG 997 | C++98 | abhängige Parametertypen und Rückgabetypen waren aus der Betrachtung bei der Bestimmung der zugehörigen Klassen und Namespaces eines Funktionstemplate ausgeschlossen |
berücksichtigt |
| CWG 1690 | C++98 C++11 |
ADL konnte keine Lambdas (C++11) oder Objekte von lokalen Klassentypen (C++98) finden, die zurückgegeben werden |
sie können gefunden werden |
| CWG 1691 | C++11 | ADL hatte überraschende Verhaltensweisen für undurchsichtige Enumerationsdeklarationen | fixed |
| CWG 1692 | C++98 | doppelt verschachtelte Klassen hatten keine zugehörigen Namespaces (ihre umschließenden Klassen sind keine Member eines Namespaces) |
zugehörige Namespaces sind erweitert auf die innersten umschließenden Namespaces |
| CWG 2857 | C++98 | die zugehörigen Klassen eines unvollständigen Klassentyps schlossen ihre Basisklassen ein |
nicht eingeschlossen |
[bearbeiten] Siehe auch
[bearbeiten] Externe Links
|