Jakie są dobre wyjaśnienia, czym jest wyszukiwanie zależne od argumentów? Wiele osób nazywa to również Koenig Lookup.
Najlepiej chciałbym wiedzieć:
- Dlaczego to dobrze?
- Dlaczego to jest zła rzecz?
- Jak to działa?
c++
argument-dependent-lookup
name-lookup
c++-faq
user965369
źródło
źródło
std::cout << "Hello world";
nie skompilowaćOdpowiedzi:
Koenig Lookup lub Argument Dependent Lookup opisuje, jak niekwalifikowane nazwy są wyszukiwane przez kompilator w C ++.
Standard C ++ 11, § 3.4.2 / 1, stwierdza:
Mówiąc prościej, Nicolai Josuttis stwierdza 1 :
Prosty przykład kodu:
W powyższym przykładzie nie ma ani
using
-declaration ani -directive,using
ale mimo to kompilator poprawnie identyfikuje niekwalifikowaną nazwędoSomething()
jako funkcję zadeklarowaną w przestrzeni nazwMyNamespace
, stosując wyszukiwanie Koeniga .Jak to działa?
Algorytm mówi kompilatorowi, aby nie tylko sprawdzał zakres lokalny, ale także przestrzenie nazw zawierające typ argumentu. Zatem w powyższym kodzie kompilator stwierdza, że obiekt
obj
będący argumentem funkcjidoSomething()
należy do przestrzeni nazwMyNamespace
. Dlatego sprawdza tę przestrzeń nazw, aby zlokalizować deklaracjędoSomething()
.Jaka jest zaleta wyszukiwania Koenig?
Jak pokazuje powyższy prosty przykład kodu, wyszukiwanie Koeniga zapewnia programiście wygodę i łatwość użycia. Bez wyszukiwania Koenig programista
using
musiałby obciążać narzut, aby wielokrotnie określać w pełni kwalifikowane nazwy, lub zamiast tego używać wielu -deklaracji.Skąd krytyka wyszukiwania Koeniga?
Nadmierne poleganie na wyszukiwaniu Koenig może prowadzić do problemów semantycznych i czasami zaskakiwać programistę.
Rozważmy przykład
std::swap
, który jest standardowym algorytmem biblioteki do zamiany dwóch wartości. W przypadku wyszukiwania Koeniga należałoby zachować ostrożność podczas korzystania z tego algorytmu, ponieważ:mogą nie zachowywać się tak samo jak:
W przypadku ADL to, która wersja
swap
funkcji zostanie wywołana, zależy od przestrzeni nazw przekazywanych do niej argumentów.Jeśli istnieje przestrzeń nazw
A
, a jeśliA::obj1
,A::obj2
iA::swap()
istnieje wówczas drugi przykład spowoduje wywołanieA::swap()
, który może nie być czego chce użytkownik.Ponadto, jeśli z jakiegoś powodu oba
A::swap(A::MyClass&, A::MyClass&)
istd::swap(A::MyClass&, A::MyClass&)
są zdefiniowane, to pierwszy przykład zostanie wywołany,std::swap(A::MyClass&, A::MyClass&)
ale drugi nie zostanie skompilowany, ponieważswap(obj1, obj2)
byłby niejednoznaczny.Drobnostki:
Dlaczego nazywa się to „wyszukiwaniem Koeniga”?
Ponieważ został opracowany przez byłego badacza i programistę AT&T i Bell Labs, Andrew Koeniga .
Czytaj dalej:
Wyszukiwanie nazwiska Herba Suttera na GotW
Standard C ++ 03/11 [basic.lookup.argdep]: 3.4.2 Wyszukiwanie nazw zależnych od argumentów.
1 Definicja wyszukiwania Koeniga jest taka, jak zdefiniowano w książce Josuttisa, The C ++ Standard Library: A Tutorial and Reference .
źródło
std::swap
rzeczywistości musisz to zrobić, ponieważ jedyną alternatywą byłoby dodaniestd::swap
jawnej specjalizacji funkcji szablonu dla TwojejA
klasy. Jednak jeśli twojaA
klasa jest sama w sobie szablonem, byłaby to częściowa specjalizacja, a nie jawna specjalizacja. Częściowa specjalizacja funkcji szablonu jest niedozwolona. Dodanie przeciążeniastd::swap
byłoby alternatywą, ale jest wyraźnie zabronione (nie możesz dodawać rzeczy dostd
przestrzeni nazw). Tak więc ADL to jedyny sposóbstd::swap
.std::swap()
wydaje się nieco cofnięty. Spodziewam się problem daj się, gdystd::swap()
wybrano zamiast specyficznego przeciążeniowym do typuA::swap()
. Przykład zstd::swap(A::MyClass&, A::MyClass&)
wydaje się mylący. ponieważstd
nigdy nie miałoby określonego przeciążenia dla typu użytkownika, nie sądzę, że jest to świetny przykład.MyNamespace::doSomething
, a nie tylko::doSomething
.W Koenig Lookup, jeśli wywoływana jest funkcja bez określenia jej przestrzeni nazw, wówczas nazwa funkcji jest również przeszukiwana w przestrzeni (ach) nazw, w których zdefiniowano typ argumentu (ów). Dlatego jest również znany jako Wyszukiwanie nazw zależne od argumentów , w skrócie po prostu ADL .
To dzięki Koenig Lookup możemy napisać tak:
W przeciwnym razie musielibyśmy napisać:
co naprawdę jest zbyt długie, a kod wygląda naprawdę brzydko!
Innymi słowy, przy braku Koenig Lookup nawet program Hello World wygląda na skomplikowany.
źródło
std::cout
jest to jeden argument funkcji, który wystarczy, aby włączyć ADL. Zauważyłeś to?ostream<<
(jak w tym, co przyjmuje jako argumenty i co zwraca). 2) W pełni kwalifikowane nazwy (takie jakstd::vector
lubstd::operator<<
). 3) Bardziej szczegółowe badanie wyszukiwania zależnego od argumentów.std::endl
jako argument, jest w rzeczywistości funkcją składową. W każdym razie, jeśli użyję"\n"
zamiaststd::endl
, moja odpowiedź jest prawidłowa. Dziękuję za komentarz.f(a,b)
wywołuje funkcję wolną . Więc w przypadkustd::operator<<(std::cout, std::endl);
nie ma takiej wolnej funkcji, która przyjmujestd::endl
jako drugi argument. Jest to funkcja składowa, która przyjmujestd::endl
jako argument i dla której musisz napisaćstd::cout.operator<<(std::endl);
. a ponieważ istnieje wolna funkcja, która przyjmujechar const*
jako drugi argument,"\n"
działa;'\n'
zadziała również.Może najlepiej zacząć od tego, dlaczego, a dopiero potem przejść do tego, jak.
Kiedy wprowadzono przestrzenie nazw, chodziło o to, aby wszystko było zdefiniowane w przestrzeniach nazw, aby oddzielne biblioteki nie kolidowały ze sobą. Jednak spowodowało to problem z operatorami. Spójrz na przykład na następujący kod:
Oczywiście mogłeś napisać
N::operator++(x)
, ale to pokonałoby cały sens przeciążenia operatora. Dlatego trzeba było znaleźć rozwiązanie, które pozwoli kompilatorowi znaleźć,operator++(X&)
mimo że nie było to w zakresie. Z drugiej strony, nadal nie powinien znaleźć innejoperator++
zdefiniowanej w innej, niepowiązanej przestrzeni nazw, która może sprawić, że wywołanie będzie niejednoznaczne (w tym prostym przykładzie nie uzyskasz niejednoznaczności, ale w bardziej złożonych przykładach możesz). Rozwiązaniem było wyszukiwanie zależne od argumentu (ADL), nazwane w ten sposób, ponieważ wyszukiwanie zależy od argumentu (a dokładniej od typu argumentu). Ponieważ schemat został wymyślony przez Andrew R. Koeniga, jest również często nazywany wyszukiwaniem Koeniga.Sztuczka polega na tym, że w przypadku wywołań funkcji, oprócz zwykłego wyszukiwania nazw (które znajduje nazwy w zakresie w miejscu użycia), wykonywane jest drugie wyszukiwanie w zakresach typów wszelkich argumentów nadanych funkcji. Tak więc w powyższym przykładzie, jeśli piszesz
x++
w głównym, szukaoperator++
nie tylko w zakresie globalnym, ale także w zakresie, w którym typx
,N::X
został zdefiniowany, tjnamespace N
. I tam znajduje dopasowanieoperator++
i dlategox++
po prostu działa. Jednakoperator++
, powiedzmyN2
, inna zdefiniowana w innej przestrzeni nazw nie zostanie znaleziona. Ponieważ ADL nie jest ograniczony do przestrzeni nazw, możesz również użyćf(x)
zamiastN::f(x)
inmain()
.źródło
Moim zdaniem nie wszystko w tym jest dobre. Ludzie, w tym sprzedawcy kompilatorów, obrażają go z powodu jego czasami niefortunnego zachowania.
ADL jest odpowiedzialny za gruntowny przegląd pętli for-range w C ++ 11. Aby zrozumieć, dlaczego ADL może czasami mieć niezamierzone skutki, weź pod uwagę nie tylko przestrzenie nazw, w których definiowane są argumenty, ale także argumenty argumentów szablonów argumentów, typów parametrów typów funkcji / typów wskaźników typów wskaźników tych argumentów i tak dalej i dalej.
Przykład użycia boost
Skutkowało to niejednoznacznością, jeśli użytkownik korzysta z biblioteki boost.range, ponieważ zarówno
std::begin
została znaleziona (przez ADL przy użyciustd::vector
), jak iboost::begin
znaleziona (przez ADL przy użyciuboost::shared_ptr
).źródło
std::begin
czyści niejednoznaczność przestrzeni nazw.