Поиск имени в зависимости от аргумента
В C++ языке программирования поиск по аргументу ( ADL ) или поиск по имени, зависящий от аргумента , [1] применяется к поиску неполного имени функции в зависимости от типов аргументов , переданных при вызове функции . Такое поведение также известно как поиск Кенига , поскольку его часто приписывают Эндрю Кенигу , хотя он не является его изобретателем. [2]
Во время поиска, зависящего от аргумента, могут выполняться поиск в других пространствах имен, не рассматриваемых при обычном поиске, при этом набор пространств имен, подлежащих поиску, зависит от типов аргументов функции. В частности, набор объявлений, обнаруженных в процессе ADL и рассматриваемых для разрешения имени функции, представляет собой объединение объявлений, найденных при обычном поиске, с объявлениями, найденными при поиске в наборе пространств имен, связанных с типами аргументов функции. .
Пример
[ редактировать ]Пример ADL выглядит так:
namespace NS {
class A {};
void f(A& a, int i) {}
} // namespace NS
int main() {
NS::A a;
f(a, 0); // Calls NS::f.
}
Несмотря на то, что основной функция не находится в пространстве имен NS, а пространство имен NS не находится в области видимости, функция НС::f(A&, интервал) находится из-за объявленных типов фактических аргументов в операторе вызова функции.
Распространенным шаблоном в стандартной библиотеке C++ является объявление перегруженных операторов, которые будут найдены таким образом. Например, эта простая программа Hello World не скомпилировалась бы, если бы не ADL:
#include <iostream>
#include <string>
int main() {
std::string str = "hello world";
std::cout << str;
}
С использованием <<
эквивалентно вызову operator<<
без std::
квалификатор. Однако в этом случае перегрузка оператора<<, которая работает для string
находится в std
пространство имен, поэтому для его использования требуется ADL.
Следующий код будет работать без ADL (который в любом случае к нему применяется):
#include <iostream>
int main() {
std::cout << 5;
}
Это работает, потому что оператор вывода целых чисел является функцией-членом std::ostream
класс, который является типом cout
.
Таким образом, компилятор интерпретирует это утверждение как
std::cout.operator<<(5);
который он может разрешить во время обычного поиска. Однако учтите, что, например, const char *
перегружен operator<<
является функцией, не являющейся членом в std
пространство имен и, следовательно, требует ADL для правильного поиска:
/* will print the provided char string as expected using ADL derived from the argument type std::cout */
operator<<(std::cout, "Hi there")
/* calls a ostream member function of the operator<< taking a void const*,
which will print the address of the provided char string instead of the content of the char string */
std::cout.operator<<("Hi there")
The std
пространство имен перегружено, не является членом operator<<
функция для обработки строк — еще один пример:
/*equivalent to operator<<(std::cout, str). The compiler searches the std namespace using ADL due to the type std::string of the str parameter and std::cout */
std::cout << str;
Как отмечает Кениг в личной заметке, [2] без ADL компилятор укажет на ошибку, сообщив, что не удалось найти operator<<
поскольку в заявлении явно не указано, что оно находится в std
пространство имен.
Интерфейсы
[ редактировать ]Функции, найденные ADL, считаются частью интерфейса класса. В стандартной библиотеке C++ несколько алгоритмов используют неквалифицированные вызовы swap
изнутри std
пространство имен. В результате общий std::swap
функция используется, если больше ничего не найдено, но если эти алгоритмы используются со сторонним классом, Foo
, найденный в другом пространстве имен, которое также содержит swap(Foo&, Foo&)
, эта перегрузка swap
будет использоваться.
Критика
[ редактировать ]Хотя ADL позволяет функциям, определенным вне класса, вести себя так, как если бы они были частью интерфейса этого класса, он делает пространства имен менее строгими и поэтому может требовать использования полных имен, когда в противном случае они не были бы необходимы. Например, стандартная библиотека C++ широко использует неквалифицированные вызовы std::swap
поменять местами два значения. Идея состоит в том, что тогда можно определить собственную версию swap
в собственном пространстве имен и будет использоваться в алгоритмах стандартной библиотеки. Другими словами, поведение
namespace N {
struct A {};
} // namespace N
A a;
A b;
std::swap(a, b);
может совпадать, а может и не совпадать с поведением
using std::swap;
swap(a, b);
(где a
и b
относятся к типу N::A
), потому что если N::swap(N::A&, N::A&)
существует, второй из приведенных выше примеров вызовет его, а первый — нет. Более того, если по какой-то причине оба N::swap(N::A&, N::A&)
и std::swap(N::A&, N::A&)
определены, то первый пример вызовет std::swap(N::A&, N::A&)
но второй не скомпилируется, потому что swap(a, b)
было бы неоднозначно.
В общем, чрезмерная зависимость от ADL может привести к семантическим проблемам. Если одна библиотека, L1
, ожидает неквалифицированных вызовов на foo(T)
иметь одно значение и другую библиотеку, L2
ожидает, что у него будет другое, тогда пространства имен теряют свою полезность. Если, однако, L1
ожидает L1::foo(T)
иметь одно значение и L2
делает то же самое, то конфликта нет, а вызывает foo(T)
должен быть полностью квалифицирован (т. L1::foo(x)
в отличие от using L1::foo; foo(x);
), чтобы ADL не мешал.
Ссылки
[ редактировать ]- ^ «Рабочий проект стандарта языка программирования C++» (PDF) . JTC1/SC22/WG21 . 19 октября 2005 г. Глава 3.4.2 – Поиск имени в зависимости от аргумента – стр. 2. Архивировано из оригинала (PDF) 14 декабря 2005 г. Проверено 13 марта 2012 г.
- ^ Перейти обратно: а б «Личное замечание о поиске по аргументам» . 3 мая 2012 г. Архивировано из оригинала 17 марта 2018 г. . Проверено 7 февраля 2014 г.