Was ist “Argument-Dependent Lookup” (auch bekannt als ADL oder “Koenig Lookup”)?

Lesezeit: 9 Minuten

Was ist Argument Dependent Lookup auch bekannt als ADL oder Koenig
Benutzer965369

Was sind einige gute Erklärungen dafür, was argumentabhängige Suche ist? Viele Leute nennen es auch Koenig Lookup.

Am liebsten würde ich wissen:

  • Warum ist es gut?
  • Warum ist es eine schlechte Sache?
  • Wie funktioniert es?

  • gotw.ca/gotw/030.htm

    – Flexo

    13. November 2011 um 13:17 Uhr

  • mögliches Duplikat von Why GCC erlaubt den Aufruf dieser Funktion, ohne zuerst ihren Namespace zu verwenden?

    – sehen

    13. November 2011 um 13:28 Uhr

  • Das ist gut so, denn sonst: std::cout << "Hello world"; würde nicht kompilieren

    – sehen

    13. November 2011 um 13:30 Uhr

  • de.cppreference.com/w/cpp/language/adl

    – Li Kui

    19. März 2018 um 9:39 Uhr

Was ist Argument Dependent Lookup auch bekannt als ADL oder Koenig
Alok Speichern

König Lookupoder Argumentabhängige Suchebeschreibt, wie unqualifizierte Namen vom Compiler in C++ nachgeschlagen werden.

Der C++11-Standard § 3.4.2/1 besagt:

Wenn der Postfix-Ausdruck in einem Funktionsaufruf (5.2.2) eine unqualifizierte ID ist, können andere Namensräume, die während der üblichen unqualifizierten Suche (3.4.1) nicht berücksichtigt werden, durchsucht werden, und in diesen Namensräumen werden Namensraumbereichsfreund-Funktionsdeklarationen ( 11.3) nicht anderweitig sichtbar gefunden werden. Diese Änderungen an der Suche hängen von den Typen der Argumente ab (und für Template-Template-Argumente vom Namespace des Template-Arguments).

Einfacher ausgedrückt sagt Nicolai Josuttis1:

Sie müssen den Namensraum für Funktionen nicht qualifizieren, wenn ein oder mehrere Argumenttypen im Namensraum der Funktion definiert sind.

Ein einfaches Codebeispiel:

namespace MyNamespace
{
    class MyClass {};
    void doSomething(MyClass) {}
}

MyNamespace::MyClass obj; // global object


int main()
{
    doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}

Im obigen Beispiel gibt es weder a using-Erklärung noch a using-Direktive, aber der Compiler identifiziert den nicht qualifizierten Namen trotzdem korrekt doSomething() als die im Namespace deklarierte Funktion MyNamespace durch Auftragen Koenig-Suche.

Wie funktioniert es?

Der Algorithmus weist den Compiler an, nicht nur den lokalen Gültigkeitsbereich zu betrachten, sondern auch die Namespaces, die den Typ des Arguments enthalten. Somit findet der Compiler im obigen Code, dass das Objekt objwas das Argument der Funktion ist doSomething()gehört zum Namensraum MyNamespace. Es sucht also in diesem Namensraum nach der Deklaration von doSomething().

Was ist der Vorteil der Koenig-Suche?

Wie das obige einfache Codebeispiel zeigt, bietet die Koenig-Suche dem Programmierer Komfort und Benutzerfreundlichkeit. Ohne die Koenig-Suche würde es für den Programmierer einen Mehraufwand geben, die vollständig qualifizierten Namen wiederholt anzugeben oder stattdessen zahlreiche zu verwenden using-Erklärungen.

Warum die Kritik an Koenig Lookup?

Sich zu sehr auf die Koenig-Suche zu verlassen, kann zu semantischen Problemen führen und den Programmierer manchmal unvorbereitet treffen.

Betrachten Sie das Beispiel von std::swap, bei dem es sich um einen Standardbibliotheksalgorithmus zum Austauschen zweier Werte handelt. Bei der Koenig-Suche müsste man bei der Verwendung dieses Algorithmus vorsichtig sein, weil:

std::swap(obj1,obj2);

zeigt möglicherweise nicht das gleiche Verhalten wie:

using std::swap;
swap(obj1, obj2);

Mit ADL, welche Version von swap Funktion aufgerufen wird, würde vom Namensraum der übergebenen Argumente abhängen.

Wenn es einen Namensraum gibt Aund wenn A::obj1, A::obj2und A::swap() existieren, dann führt das zweite Beispiel zu einem Aufruf an A::swap()was möglicherweise nicht das ist, was der Benutzer wollte.

Ferner, wenn aus irgendeinem Grund beides A::swap(A::MyClass&, A::MyClass&) und std::swap(A::MyClass&, A::MyClass&) definiert sind, dann wird das erste Beispiel aufgerufen std::swap(A::MyClass&, A::MyClass&) aber die zweite wird nicht kompiliert, weil swap(obj1, obj2) wäre zweideutig.

Wissenswertes:

Warum heißt es „Koenig-Lookup“?

Da es von einem ehemaligen Forscher und Programmierer von AT&T und Bell Labs entwickelt wurde, Andreas König.

Weiterlesen:


**1** Die Definition der Koenig-Suche ist wie in Josuttis’ Buch *The C++ Standard Library: A Tutorial and Reference* definiert.

  • @AlokSave: +1 für die Antwort, aber die Kleinigkeiten sind nicht korrekt. Koenig hat ADL nicht erfunden, as er gesteht hier 🙂

    – legends2k

    28. Juli 2014 um 7:30 Uhr

  • Das Beispiel in der Kritik am Koenig-Algorithmus kann sowohl als “Feature” der Koenig-Suche als auch als “Betrug” angesehen werden. Die Verwendung von std::swap() auf diese Weise ist eine gängige Redewendung: Stellen Sie ein ‘using std::swap()’ bereit, falls eine spezialisiertere Version A::swap() nicht bereitgestellt wird. Wenn eine spezialisierte Version von A::swap() verfügbar ist, würden wir normalerweise will der angerufen werden soll. Dies bietet mehr Generizität für den Aufruf von swap(), da wir darauf vertrauen können, dass der Aufruf kompiliert und funktioniert, aber wir können auch darauf vertrauen, dass die spezialisiertere Version verwendet wird, falls es eine gibt.

    – Anthony Halle

    9. Dezember 2014 um 19:16 Uhr

  • @anthrond Da steckt mehr drin. Mit std::swap Sie müssen das tatsächlich tun, da die einzige Alternative das Hinzufügen wäre std::swap Vorlagenfunktion explizite Spezialisierung für Ihre A Klasse. Doch wenn Ihr A Klasse selbst eine Vorlage ist, wäre es eher eine partielle Spezialisierung als eine explizite Spezialisierung. Und eine teilweise Spezialisierung der Vorlagenfunktion ist nicht zulässig. Überladung von hinzufügen std::swap wäre eine Alternative, ist aber ausdrücklich verboten (Sie dürfen keine Dinge hinzufügen std Namensraum). Also ADL ist das einzige Weg für std::swap.

    – Adam Badura

    10. April 2015 um 11:23 Uhr

  • Ich hätte erwartet, dass überladene Operatoren unter “Vorteil der Koenig-Suche” erwähnt werden. das Beispiel mit std::swap() wirkt etwas rückständig. Ich würde erwarten, dass das Problem wann sein wird std::swap() statt der typspezifischen Überladung ausgewählt wird, A::swap(). Das Beispiel mit std::swap(A::MyClass&, A::MyClass&) scheint irreführend. seit std würde niemals eine spezifische Überladung für einen Benutzertyp haben, ich denke nicht, dass es ein gutes Beispiel ist.

    – Arvid

    24. Oktober 2016 um 16:26 Uhr

  • @gsamaras …Und? Wir können alle sehen, dass die Funktion nie definiert wurde. Ihre Fehlermeldung beweist, dass es tatsächlich funktioniert hat, denn es wird gesucht MyNamespace::doSomethingnicht nur ::doSomething.

    – Nik

    12. Juni 2018 um 23:47 Uhr

1647268217 60 Was ist Argument Dependent Lookup auch bekannt als ADL oder Koenig
Nawaz

Wenn in der Koenig-Suche eine Funktion aufgerufen wird, ohne ihren Namensraum anzugeben, dann ist der Name einer Funktion Auch gesucht in Namespace(s), in denen der Typ des Arguments/der Argumente definiert ist. Deshalb wird es auch als bezeichnet Argumentabhängige Namenssuchekurz einfach ADL.

Aufgrund von Koenig Lookup können wir Folgendes schreiben:

std::cout << "Hello World!" << "\n";

Sonst müssten wir schreiben:

std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");

das ist wirklich zu viel Tippen und der Code sieht wirklich hässlich aus!

Mit anderen Worten, in Ermangelung von Koenig Lookup sogar a Hallo Welt Programm sieht kompliziert aus.

  • Das Beispiel ist eigentlich falsch. Es wäre richtig, wenn (zum Beispiel) std::string wurde anstelle von String-Literal verwendet. Siehe Koenigs Erklärung: drdobbs.com/cpp/a-personal-note-about-argument-dependent/…

    – Adam Badura

    10. April 2015 um 11:54 Uhr

  • @AdamBadura: Bitte beachten Sie das std::cout ist ein Argument für die Funktion, das ausreicht, um ADL zu aktivieren. Hast du das gemerkt?

    – Nawaz

    10. April 2015 um 15:58 Uhr

  • @meet: Ihre Frage erfordert eine lange Antwort, die an dieser Stelle nicht gegeben werden kann. Ich kann Ihnen also nur raten, sich mit Themen wie: 1) Unterschrift von zu befassen ostream<< (was es als Argumente braucht und was es zurückgibt). 2) Vollqualifizierte Namen (wie std::vector oder std::operator<<). 3) Eine detailliertere Untersuchung des argumentabhängigen Nachschlagens.

    – Nawaz

    27. Mai 2015 um 3:26 Uhr

  • @WorldSEnder: Ja, du hast Recht. Die Funktion, die übernehmen kann std::endl als Argument ist eigentlich eine Member-Funktion. Wie auch immer, wenn ich benutze "\n" anstatt std::endl, dann ist meine Antwort richtig. Danke für den Kommentar.

    – Nawaz

    16. Juli 2015 um 7:04 Uhr


  • @Destructor: Weil ein Funktionsaufruf der Form von f(a,b) ruft a auf kostenlos Funktion. Also im Falle von std::operator<<(std::cout, std::endl);gibt es keine solche freie Funktion, die dauert std::endl als zweites Argument. Es ist die Mitgliedsfunktion, die übernimmt std::endl als Argument, und für das Sie schreiben müssen std::cout.operator<<(std::endl);. und da gibt es a kostenlos Funktion, die dauert char const* Als zweites Argument "\n" Werke; '\n' würde auch funktionieren.

    – Nawaz

    6. Juni 2016 um 5:42 Uhr


Was ist Argument Dependent Lookup auch bekannt als ADL oder Koenig
celtschk

Vielleicht ist es am besten, mit dem Warum zu beginnen und erst dann zum Wie überzugehen.

Als Namespaces eingeführt wurden, war die Idee, alles in Namespaces zu definieren, damit separate Bibliotheken sich nicht gegenseitig stören. Dies führte jedoch zu einem Problem mit den Operatoren. Sehen Sie sich zum Beispiel den folgenden Code an:

namespace N
{
  class X {};
  void f(X);
  X& operator++(X&);
}

int main()
{
  // define an object of type X
  N::X x;

  // apply f to it
  N::f(x);

  // apply operator++ to it
  ???
}

Natürlich hättest du schreiben können N::operator++(x), aber das hätte den eigentlichen Punkt der Operatorüberladung zunichte gemacht. Daher musste eine Lösung gefunden werden, die es dem Compiler ermöglichte, zu finden operator++(X&) trotz der Tatsache, dass es nicht im Geltungsbereich war. Auf der anderen Seite sollte es immer noch keinen anderen finden operator++ in einem anderen, nicht verwandten Namensraum definiert, wodurch der Aufruf mehrdeutig werden könnte (in diesem einfachen Beispiel würden Sie keine Mehrdeutigkeit erhalten, in komplexeren Beispielen jedoch möglicherweise). Die Lösung war Argument Dependent Lookup (ADL), so genannt, da die Suche vom Argument abhängt (genauer gesagt vom Typ des Arguments). Da das Schema von Andrew R. Koenig erfunden wurde, wird es auch oft Koenig-Lookup genannt.

Der Trick besteht darin, dass für Funktionsaufrufe zusätzlich zur normalen Namenssuche (die Namen im Geltungsbereich zum Zeitpunkt der Verwendung findet) eine zweite Suche in den Geltungsbereichen der Typen aller an die Funktion übergebenen Argumente durchgeführt wird. Also im obigen Beispiel, wenn Sie schreiben x++ in der Hauptsache sucht es operator++ nicht nur im globalen Geltungsbereich, sondern auch in dem Geltungsbereich, in dem der Typ von x, N::Xwurde definiert, dh in namespace N. Und dort findet es ein passendes operator++und deshalb x++ funktioniert einfach. Andere operator++ in einem anderen Namensraum definiert, sagen wir N2, wird jedoch nicht gefunden. Da ADL nicht auf Namensräume beschränkt ist, können Sie auch verwenden f(x) anstatt N::f(x) in main().

  • Danke! Ich habe nie wirklich verstanden, warum es dort war!

    – Benutzer965369

    13. November 2011 um 15:25 Uhr

Nicht alles daran ist meiner Meinung nach gut. Leute, einschließlich Compiler-Anbieter, haben es wegen seines manchmal unglücklichen Verhaltens beleidigt.

ADL ist für eine umfassende Überarbeitung der for-Range-Schleife in C++11 verantwortlich. Um zu verstehen, warum ADL manchmal unbeabsichtigte Auswirkungen haben kann, bedenken Sie, dass nicht nur die Namespaces, in denen die Argumente definiert sind, berücksichtigt werden, sondern auch die Argumente von Template-Argumenten der Argumente, von Parametertypen von Funktionstypen / Pointee-Typen von Zeigertypen dieser Argumente , und so weiter und fort.

Ein Beispiel mit Boost

std::vector<boost::shared_ptr<int>> v;
auto x = begin(v);

Dies führte zu einer Unklarheit, wenn der Benutzer die boost.range-Bibliothek verwendet, da beide std::begin gefunden wird (von ADL mit std::vector) und boost::begin gefunden wird (von ADL mit boost::shared_ptr).

1001950cookie-checkWas ist “Argument-Dependent Lookup” (auch bekannt als ADL oder “Koenig Lookup”)?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy