Historia pytania: ADL (Argument Dependent Lookup), znane również jako Koenig lookup, po raz pierwszy pojawiło się w C++ w celu wsparcia przeciążania operatorów (szczególnie operatorów << i >> dla typów użytkownika). Celem jest poprawne znajdowanie funkcji przy mieszaniu przestrzeni nazw i typów użytkownika.
Problem: Standardowy mechanizm wyszukiwania funkcji może "nie zauważyć" funkcji, jeśli jest ona zadeklarowana w innym namespace niż punkt wywołania. ADL rozwiązuje ten problem: kompilator uwzględnia przestrzeń nazw typów argumentów funkcji, aby rozwiązać nazwy. Ten sam mechanizm czasami prowadzi do nieoczekiwanego wyboru przeciążenia lub niejednoznaczności.
Rozwiązanie: Kiedy wywoływana jest funkcja, której argumentami są obiekty z niestandardowych przestrzeni nazw, kompilator wyszukuje odpowiednie przeciążone funkcje nie tylko w bieżącym zasięgu, ale także we wszystkich przestrzeniach nazw związanych z typami argumentów.
Przykład kodu:
namespace lib { struct Widget {}; void do_something(const Widget&) { std::cout << "Widget" << std::endl; } } using lib::Widget; void call(const Widget& w) { do_something(w); // do_something znajduje się przez ADL }
Kluczowe cechy:
Jeśli zadeklarujesz funkcję o tej samej nazwie w dwóch namespace i przekażesz obiekt drugiego, która z nich zostanie wywołana?
Funkcja z przestrzeni nazw argumentu, jeśli pasuje do argumentów, zostanie wybrana dzięki ADL:
namespace A { struct S {}; void f(const S&) { std::cout << "A!"; } } namespace B { struct S {}; void f(const S&) { std::cout << "B!"; } } A::S a; B::S b; f(a); // wywoła A::f przez ADL f(b); // wywoła B::f przez ADL
Czy ADL działa z funkcjami szablonowymi?
Tak, jeżeli funkcja szablonowa jest zadeklarowana w namespace odpowiadającym typowi argumentu, ADL znajdzie ją przy wywołaniu z tym typem.
Czy ADL będzie działać dla wskaźników do funkcji?
Nie, ADL nie ma zastosowania przy uzyskiwaniu wskaźnika do funkcji (na przykład podczas pobierania jej adresu). Tylko przy bezpośrednim wywołaniu funkcji.
Projekt podłączył kilka zewnętrznych bibliotek, gdzie w każdym namespace była własna print(). W głównym kodzie używano print() z obiektami różnych klas. Z powodu ADL kompilator zaczął "wybierać" funkcję z niewłaściwej przestrzeni nazw.
Zalety:
Wady:
Użycie qualified call (jawne wskazanie namespace):
lib::do_something(w); // Jawnie!
Zalety:
Wady: