История вопроса: ADL (Argument Dependent Lookup), также известный как Koenig lookup, впервые появился в C++ для поддержки перегрузки операторов (особенно operator<< и operator>> для пользовательских типов). Цель — корректно находить функции при смешивании пространства имён и пользовательских типов.
Проблема: Стандартный механизм поиска функций может "не заметить" функцию, если она объявлена в другом namespace, нежели point вызова. ADL решает эту проблему: компилятор учитывает пространство имён типов аргументов функции, чтобы разрешить имена. Этот же механизм иногда приводит к неожиданному выбору перегрузки или неоднозначностям.
Решение: Когда вызывается функция, аргументы которой — объекты из пользовательских пространств имён, компилятор ищет подходящие перегруженные функции не только в текущем scope, но и во всех пространствах имён, связанных с типами аргументов.
Пример кода:
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 находится через ADL }
Ключевые особенности:
Если объявить функцию с таким же именем в двух namespace и передать объект второго, какая из них будет вызвана?
Функция из пространства имён аргумента, если она подходит по аргументам, будет выбрана благодаря 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); // вызовет A::f через ADL f(b); // вызовет B::f через ADL
Работает ли ADL с шаблонными функциями?
Да, если шаблонная функция определена в namespace, совпадающем с типом аргумента, ADL найдёт её при вызове с этим типом.
Будет ли работать ADL для указателей на функции?
Нет, ADL не применяется при получении указателя на функцию (например, при взятии её адреса). Только при непосредственном вызове функции.
Проект подключил несколько сторонних библиотек, где в каждом namespace был свой print(). В основном коде использовался print() с объектами разных классов. Из-за ADL компилятор начал "выбирать" функцию из не того пространства имён.
Плюсы:
Минусы:
Использование qualified call (указание namespace явно):
lib::do_something(w); // Явно!
Плюсы:
Минусы: