В каких ситуациях срабатывает аргументный поиск по имени (ADL)?
В статье Википедии ниже цитата упоминается:
ADL происходит только в том случае, если при обычном поиске безусловного имени не удается найти соответствующую функцию-член класса. В этом случае можно искать другие пространства имен, не рассматриваемые при обычном поиске, где набор пространств имен, которые нужно искать, зависит от типов аргументов функции.
Итак, я ожидал, что приведенная ниже программа скомпилируется нормально, но это не так:
namespace N1 {
class A {};
void foo (A *p) {}
}
namespace N2 {
void foo (N1::A &p) {}
}
int main () {
N1::A xa;
foo(&xa); // ok
foo(xa); // error: cannot convert ‘N1::A’ to ‘N1::A*’ for argument ‘1’ to ‘void N1::foo(N1::A*)’
}
Я искал несколько вопросов в SO, но не смог найти список требований или ситуаций в простом слове, который предлагает: когда ADL вступает в силу?
Чуть более подробный ответ будет очень полезен для меня и будущих посетителей.
5 ответов
Это не должно компилироваться. A
находится в пространстве имен N1
, Как компилятор должен знать, что вы хотите вызвать N2::foo
?
n3376 3.4.2/2
Для каждого типа аргумента T в вызове функции существует набор из нуля или более связанных пространств имен и набор из нуля или более связанных классов, которые необходимо учитывать. Наборы пространств имен и классов полностью определяются типами аргументов функции (и пространством имен любого аргумента шаблона шаблона). Имена типов и объявления-использования, используемые для указания типов, не вносят свой вклад в этот набор. Наборы пространств имен и классов определяются следующим образом:
Если T является типом класса (включая объединения), его ассоциированными классами являются: сам класс; класс, членом которого он является, если таковой имеется; и его прямые и косвенные базовые классы. Его связанные пространства имен - это пространства имен, членами которых являются связанные классы. Кроме того, если T является специализацией шаблона класса, его связанные пространства имен и классы также включают: пространства имен и классы, связанные с типами аргументов шаблона, предоставляемых для параметров типа шаблона (исключая параметры шаблона шаблона); пространства имен, членами которых являются любые аргументы шаблона шаблона; и классы, членами которых являются любые шаблоны-члены, используемые в качестве аргументов шаблона. [Примечание: нетипичные аргументы шаблона не вносят вклад в набор связанных пространств имен. - конец примечания]
Если поиск по имени не дает результатов, поиск продолжается с использованием аргумента вызова функции.
пример
func(A x);
Затем компилятор будет смотреть на пространство имен, начиная с пространства имен, включая класс А. Один из примеров:
// argument_dependent_name_koenig_lookup_on_functions.cpp
namespace A
{
struct X
{
};
void f(const X&)
{
}
}
int main()
{
// The compiler finds A::f() in namespace A, which is where
// the type of argument x is defined. The type of x is A::X.
A::X x;
f(x);
}
Подробнее здесь http://msdn.microsoft.com/en-us/library/60bx1ys7.aspx
ADL включается почти всегда, как только ваша функция принимает пользовательский тип. Это начинает foo
Вот: xa
определяется в N1
, так foo
ищется в N1
а также в глобальном пространстве имен. (Без ADL, foo
будет искать только в глобальном пространстве имен.)
И я не понимаю, почему вы ожидаете второго звонка foo
Скомпилировать. Тип xa
определяется в N1
так ADL добавляет N1
к пути поиска, но в выражении нет ничего, что могло бы подразумевать N2
,
Это говорит, что "другие пространства имен" ищутся. Это не говорит, что "все пространства имен" ищутся.
Правила для того, что дополнительные пространства имен включены в ADL, немного сложны, но наиболее важным является пространство имен, в котором A
определено. Вот почему ваш первый foo
найден. Ваш второй foo
не может быть найден, потому что пространство имен N2
не имеет ничего общего с ADL.
Компилятор останавливает поиск, как только он найдет функцию с соответствующим именем. Он не продолжает поиск, если типы аргументов или специальные возможности (общедоступные / защищенные / приватные) фактически не позволяют использовать функцию в текущем контексте. Следовательно, в вашем примере компилятор не имеет изменений, чтобы "увидеть" N2::foo
, поскольку N1::foo
найден первым
Обратите внимание, что в вашем примере N2::foo
не будет найден, даже если N1::foo
не существует, так как у вас нет ссылки на N2
где-нибудь внутри main
, так N2
не будет искать вообще.