Argument-dependent lookup
Поиск, зависимый от аргументов (ADL), также известный как поиск Кёнига [1] , представляет собой набор правил для поиска неквалифицированных имён функций в выражениях вызова функций , включая неявные вызовы функций для перегруженных операторов . Эти имена функций ищутся в пространствах имён их аргументов в дополнение к областям видимости и пространствам имён, рассматриваемым обычным неквалифицированным поиском имён .
Поиск, зависимый от аргументов (argument-dependent lookup), позволяет использовать операторы, определенные в другом пространстве имен. Пример:
#include <iostream> int main() { std::cout << "Test\n"; // В глобальном пространстве имен нет operator<<, но ADL // проверяет пространство имен std, потому что левый аргумент находится // в std, и находит std::operator<<(std::ostream&, const char*) operator<<(std::cout, "Test\n"); // То же самое, с использованием синтаксиса вызова функции // Однако, std::cout << endl; // Ошибка: “endl” не объявлен в этом пространстве имен. // Это не вызов функции endl(), поэтому ADL не применяется endl(std::cout); // OK: это вызов функции: ADL проверяет пространство имен std // потому что аргумент endl находится в std, и находит std::endl (endl)(std::cout); // Ошибка: “endl” не объявлен в этом пространстве имен. // Подвыражение (endl) не является неквалифицированным идентификатором }
Содержание |
Детали
Во-первых, поиск, зависимый от аргументов, не рассматривается, если набор поиска, полученный обычным unqualified lookup содержит любой из следующих элементов:
В противном случае, для каждого аргумента в выражении вызова функции его тип анализируется, чтобы определить ассоциированный набор пространств имён и классов , который будет добавлен к поиску.
T
или указатель на массив
T
, тип
T
анализируется, и его ассоциированный набор классов и пространств имён добавляется в набор.
F
класса
X
, типы параметров функции, тип возвращаемого значения функции и класс
X
анализируются, и их ассоциированный набор классов и пространств имён добавляется в набор.
T
класса
X
, тип члена и тип
X
оба анализируются, и их ассоциированные наборы классов и пространств имён добавляются в множество.
- Дополнительно, если набор перегрузок задан через шаблонный идентификатор , все его аргументы шаблона типа и шаблоны шаблонов (но не константные аргументы шаблона) исследуются, и их ассоциированный набор классов и пространств имён добавляется в множество.
|
Если любое пространство имён в ассоциированном наборе классов и пространств имён является inline namespace , его охватывающее пространство имён также добавляется в набор. Если любое пространство имён в ассоциированном наборе классов и пространств имён непосредственно содержит inline namespace, этот inline namespace добавляется в набор. |
(since C++11) |
После определения соответствующего набора классов и пространств имен все объявления, найденные в классах этого набора, отбрасываются для целей дальнейшей обработки ADL, за исключением friend-функций и шаблонов функций в области пространства имен, как указано в пункте 2 ниже.
Набор объявлений, найденных с помощью обычного unqualified lookup и набор объявлений, найденных во всех элементах ассоциированного набора, полученного с помощью ADL, объединяются со следующими специальными правилами:
Примечания
Из-за поиска, зависимого от аргументов (argument-dependent lookup), функции и операторы, не являющиеся членами класса и определённые в том же пространстве имён, что и класс, считаются частью публичного интерфейса этого класса (если они найдены через ADL) [2] .
ADL — это причина устоявшейся идиомы для обмена двух объектов в обобщённом коде:
using
std::
swap
;
swap
(
obj1, obj2
)
;
потому что прямой вызов
std::
swap
(
obj1, obj2
)
не учитывает пользовательские функции
swap()
, которые могут быть определены в том же пространстве имён, что и типы
obj1
или
obj2
, а простой вызов без квалификации
swap
(
obj1, obj2
)
ничего не вызовет, если пользовательская перегрузка не предоставлена. В частности,
std::iter_swap
и все другие алгоритмы стандартной библиотеки используют этот подход при работе с
Swappable
типами.
Правила поиска имен делают непрактичным объявление операторов в глобальном или пользовательском пространстве имен, которые работают с типами из пространства имен
std
, например, пользовательского
operator
>>
или
operator
+
для
std::vector
или для
std::pair
(за исключением случаев, когда типы элементов vector/pair являются пользовательскими типами, которые добавляют свое пространство имен в ADL). Такие операторы не будут найдены при инстанцировании шаблонов, таких как алгоритмы стандартной библиотеки. Дополнительные сведения см. в разделе
зависимые имена
.
ADL может найти friend function (обычно перегруженный оператор), которая полностью определена внутри класса или шаблона класса, даже если она никогда не была объявлена на уровне пространства имен.
template<typename T> struct number { number(int); friend number gcd(number x, number y) { return 0; }; // Определение внутри // шаблона класса }; // Без соответствующего объявления gcd является // невидимым (кроме ADL) членом этого пространства имен void g() { number<double> a(3), b(4); a = gcd(a, b); // Находит gcd, потому что number<double> является ассоциированным классом, // делая gcd видимым в его пространстве имен (глобальная область видимости) // b = gcd(3, 4); // Ошибка; gcd не виден }
|
Хотя вызов функции может быть разрешен через ADL, даже если обычный поиск ничего не находит, вызов функции для шаблона функции с явно указанными аргументами шаблона требует, чтобы было найдено объявление шаблона с помощью обычного поиска (в противном случае, это синтаксическая ошибка - встретить неизвестное имя, за которым следует символ "меньше"). namespace N1 { struct S {}; template<int X> void f(S); } namespace N2 { template<class T> void f(T t); } void g(N1::S s) { f<3>(s); // Syntax error until C++20 (unqualified lookup finds no f) N1::f<3>(s); // OK, qualified lookup finds the template 'f' N2::f<3>(s); // Error: N2::f does not take a constant parameter // N1::f is not looked up because ADL only works // with unqualified names using N2::f; f<3>(s); // OK: Unqualified lookup now finds N2::f // then ADL kicks in because this name is unqualified // and finds N1::f } |
(до C++20) |
В следующих контекстах происходит только ADL-поиск (то есть поиск только в ассоциированных пространствах имен):
|
(since C++11) |
- поиск зависимого имени dependent name lookup из точки инстанцирования шаблона.
|
(начиная с C++17) |
Примеры
|
Этот раздел не завершён
Причина: больше примеров |
Пример из http://www.gotw.ca/gotw/030.htm
namespace A { struct X; struct Y; void f(int); void g(X); } namespace B { void f(int i) { f(i); // Вызывает B::f (бесконечная рекурсия) } void g(A::X x) { g(x); // Ошибка: неоднозначность между B::g (обычный поиск) // и A::g (поиск, зависимый от аргументов) } void h(A::Y y) { h(y); // Вызывает B::h (бесконечная рекурсия): ADL проверяет пространство имен A // но не находит A::h, поэтому используется только B::h из обычного поиска } }
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 33 | C++98 |
связанные пространства имен или классы не указаны,
если аргумент для поиска - адрес группы перегруженных функций или шаблона функции |
указано |
| CWG 90 | C++98 |
связанные классы вложенного не-объединения
не включали его объемлющий класс, но вложенное объединение было связано с его объемлющим классом |
не-объединения также связаны |
| CWG 239 | C++98 |
объявление функции в области видимости блока, найденное
при обычном неподготовленном поиске, не предотвращало ADL |
ADL не рассматривается, кроме
случаев с using объявлениями |
| CWG 997 | C++98 |
зависимые типы параметров и возвращаемые типы были
исключены из рассмотрения при определении связанных классов и пространств имен шаблона функции |
включены |
| CWG 1690 |
C++98
C++11 |
ADL не мог находить лямбды (C++11) или объекты
типов локальных классов (C++98), которые возвращаются |
они могут быть найдены |
| CWG 1691 | C++11 | ADL имел неожиданное поведение для непрозрачных объявлений перечислений | исправлено |
| CWG 1692 | C++98 |
дважды вложенные классы не имели связанных пространств имен
(их объемлющие классы не являются членами какого-либо пространства имен) |
связанные пространства имен
расширены до самых внутренних объемлющих пространств имен |
| CWG 2857 | C++98 |
связанные классы неполного
типа класса включали его базовые классы |
не включаются |
Смотрите также
Внешние ссылки
|