В каких ситуациях срабатывает аргументный поиск по имени (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 не будет искать вообще.

Другие вопросы по тегам