Was sind einige gute Erklärungen für die argumentabhängige Suche? Viele Leute nennen es auch Koenig Lookup.
Am liebsten würde ich wissen:
- Warum ist es eine gute Sache?
- Warum ist es eine schlechte Sache?
- Wie funktioniert es?
c++
argument-dependent-lookup
name-lookup
c++-faq
user965369
quelle
quelle
std::cout << "Hello world";
würde nicht kompilierenAntworten:
Koenig Lookup oder Argument Dependent Lookup beschreibt, wie unqualifizierte Namen vom Compiler in C ++ gesucht werden.
Der C ++ 11-Standard § 3.4.2 / 1 besagt:
Einfacher ausgedrückt sagt Nicolai Josuttis 1 :
Ein einfaches Codebeispiel:
Im obigen Beispiel gibt es weder eine
using
-deklaration noch eineusing
-directive, aber der Compiler identifiziert den nicht qualifizierten Namen korrektdoSomething()
als die im Namespace deklarierte Funktion,MyNamespace
indem er die Koenig-Suche anwendet .Wie funktioniert es?
Der Algorithmus weist den Compiler an, nicht nur den lokalen Bereich zu betrachten, sondern auch die Namespaces, die den Typ des Arguments enthalten. Somit stellt der Compiler im obigen Code fest, dass das Objekt
obj
, das das Argument der Funktion istdoSomething()
, zum Namespace gehörtMyNamespace
. Es sieht sich also diesen Namespace an, um die Deklaration von zu findendoSomething()
.Was ist der Vorteil der Koenig-Suche?
Wie das obige einfache Codebeispiel zeigt, bietet die Koenig-Suche dem Programmierer Komfort und Benutzerfreundlichkeit. Ohne Koenig-Suche würde der Programmierer einen Overhead haben, um die vollständig qualifizierten Namen wiederholt anzugeben oder stattdessen zahlreiche
using
Deklarationen zu verwenden.Warum die Kritik an Koenig Lookup?
Übermäßiges Vertrauen in die Koenig-Suche kann zu semantischen Problemen führen und den Programmierer manchmal überraschen.
Betrachten Sie das Beispiel
std::swap
eines Standardbibliotheksalgorithmus zum Austauschen von zwei Werten. Bei der Koenig-Suche müsste man bei der Verwendung dieses Algorithmus vorsichtig sein, weil:zeigt möglicherweise nicht das gleiche Verhalten wie:
Bei ADL
swap
hängt die Version der aufgerufenen Funktion vom Namespace der an ADL übergebenen Argumente ab.Wenn es einen Namespace gibt
A
und wennA::obj1
,A::obj2
&A::swap()
existiert, führt das zweite Beispiel zu einem Aufruf vonA::swap()
, was möglicherweise nicht das ist, was der Benutzer wollte.Wenn aus irgendeinem Grund beide
A::swap(A::MyClass&, A::MyClass&)
undstd::swap(A::MyClass&, A::MyClass&)
definiert sind, wird das erste Beispiel aufgerufenstd::swap(A::MyClass&, A::MyClass&)
, das zweite jedoch nicht kompiliert, daswap(obj1, obj2)
dies mehrdeutig wäre.Wissenswertes:
Warum heißt es "Koenig Lookup"?
Weil es vom ehemaligen Forscher und Programmierer Andrew Koenig von AT & T und Bell Labs entwickelt wurde .
Weiterführende Literatur:
Herb Sutters Namenssuche auf GotW
Standard C ++ 03/11 [basic.lookup.argdep]: 3.4.2 Argumentabhängige Namenssuche.
1 Die Definition von Koenig Lookup ist wie in Josuttis 'Buch The C ++ Standard Library: A Tutorial and Reference definiert .
quelle
std::swap
Sie tatsächlich tun, da die einzige Alternative darin besteht, einestd::swap
explizite Spezialisierung der Vorlagenfunktion für IhreA
Klasse hinzuzufügen . Wenn IhreA
Klasse jedoch selbst eine Vorlage ist, handelt es sich eher um eine Teilspezialisierung als um eine explizite Spezialisierung. Eine teilweise Spezialisierung der Vorlagenfunktion ist nicht zulässig. Das Hinzufügen einer Überladung vonstd::swap
wäre eine Alternative, ist jedoch ausdrücklich verboten (Sie dürfen demstd
Namespace keine Dinge hinzufügen ). ADL ist also der einzige Weg fürstd::swap
.std::swap()
scheint etwas rückwärts. Ich würde erwarten, dass das Problem eher beistd::swap()
Auswahl als bei typspezifischer Überlastung liegtA::swap()
. Das Beispiel mitstd::swap(A::MyClass&, A::MyClass&)
scheint irreführend. Dastd
es für einen Benutzertyp niemals eine bestimmte Überlastung geben würde, halte ich dies nicht für ein gutes Beispiel.MyNamespace::doSomething
nicht nur sucht::doSomething
.Wenn in Koenig Lookup eine Funktion aufgerufen wird, ohne ihren Namespace anzugeben, wird der Name einer Funktion auch in Namespace (s) gesucht, in denen der Typ der Argumente definiert ist. Aus diesem Grund wird es auch als argumentabhängige Namenssuche bezeichnet , kurz ADL .
Es ist wegen Koenig Lookup, wir können dies schreiben:
Sonst müssten wir schreiben:
Das ist wirklich zu viel Tippen und der Code sieht wirklich hässlich aus!
Mit anderen Worten, ohne Koenig Lookup sieht sogar ein Hello World- Programm kompliziert aus.
quelle
std::cout
ein Argument für die Funktion ist, das ausreicht, um ADL zu aktivieren. Hast du das bemerkt?ostream<<
(wie in dem, was als Argumente verwendet wird und was zurückgegeben wird). 2) Vollqualifizierte Namen (wiestd::vector
oderstd::operator<<
). 3) Eine detailliertere Untersuchung der argumentabhängigen Suche.std::endl
als Argument verwendet werden kann, ist tatsächlich eine Mitgliedsfunktion. Wie auch immer, wenn ich"\n"
anstelle von verwendestd::endl
, ist meine Antwort richtig. Danke für den Kommentar.f(a,b)
eine freie Funktion aufruft . Im Falle vonstd::operator<<(std::cout, std::endl);
gibt es also keine solche freie Funktion, diestd::endl
als zweites Argument dient. Es ist die Member-Funktion, diestd::endl
als Argument dient und für die Sie schreiben müssenstd::cout.operator<<(std::endl);
. und da es eine freie Funktion gibt, diechar const*
als zweites Argument dient,"\n"
funktioniert;'\n'
würde auch funktionieren.Vielleicht ist es am besten, mit dem Warum zu beginnen und erst dann zum Wie zu gehen.
Bei der Einführung von Namespaces bestand die Idee darin, alles in Namespaces zu definieren, damit sich separate Bibliotheken nicht gegenseitig stören. Dies führte jedoch zu einem Problem mit den Bedienern. Schauen Sie sich zum Beispiel den folgenden Code an:
Natürlich hätten Sie schreiben können
N::operator++(x)
, aber das hätte den ganzen Punkt der Überlastung des Bedieners zunichte gemacht. Daher musste eine Lösung gefunden werden, die es dem Compiler ermöglichte,operator++(X&)
trotz der Tatsache, dass es nicht im Umfang war, zu finden. Auf der anderen Seite sollte es immer noch keinen anderen findenoperator++
, der in einem anderen, nicht verwandten Namespace definiert ist, was den Aufruf möglicherweise mehrdeutig macht (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 als Koenig-Suche bezeichnet.Der Trick besteht darin, dass für Funktionsaufrufe zusätzlich zur normalen Namenssuche (bei der Namen im Gültigkeitsbereich am Verwendungsort gefunden werden) eine zweite Suche in den Bereichen der Typen von Argumenten durchgeführt wird, die der Funktion gegeben wurden. Wenn Sie also im obigen Beispiel
x++
in main schreiben , wirdoperator++
nicht nur im globalen Bereich gesucht, sondern auch in dem Bereich, in dem der Typ vonx
,N::X
definiert wurde, dh innamespace N
. Und dort findet es eine Übereinstimmungoperator++
undx++
funktioniert daher einfach. Ein andereroperator++
, der beispielsweise in einem anderen Namespace definiert istN2
, wird jedoch nicht gefunden. Da ADL nicht auf Namespaces beschränkt ist, können Sie auchf(x)
anstelle vonN::f(x)
in verwendenmain()
.quelle
Meiner Meinung nach ist nicht alles 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, sollten Sie berücksichtigen, dass nicht nur die Namespaces berücksichtigt werden, in denen die Argumente definiert sind, sondern auch die Argumente der Vorlagenargumente der Argumente, der Parametertypen der Funktionstypen / der Zeigertypen der Zeigertypen dieser Argumente und so weiter und so fort.
Ein Beispiel mit Boost
Dies führte zu einer Mehrdeutigkeit, wenn der Benutzer die Bibliothek boost.range verwendet, da beide
std::begin
gefunden werden (durch ADL-Verwendungstd::vector
) undboost::begin
gefunden werden (durch ADL-Verwendungboost::shared_ptr
).quelle
std::begin
löscht die Mehrdeutigkeit des Namespaces.