Ответ 1
tl; dr Foo<int>
не вызывает какой-либо ADL, но Foo<X>
будет (где X
- тип класса).
Прежде всего, в этом коде foobar
есть зависимое имя из-за (С++ 14/N3936) [temp.dep]/1
В выражении вида:
postfix-expression ( expression-list opt )
где постфиксное выражение является неквалифицированным-id, unqualified-id обозначает зависимое имя, если [...]
- любое из выражений в списке выражений является зависимым от типа выражением (14.6.2.2) или
и t
- зависимое имя, потому что оно является частью объявления T t
, где t
является параметром шаблона и, следовательно, зависимым типом.
Переходя на зависимое разрешение имен, существует [temp.dep.res]/1
, в котором говорится о том, что имена могут отображаться как в контексте определения, так и в контексте контекста, и определяет, где контекст создания. Я пропустил это для краткости, но в этом примере template class Foo<int>;
является точкой инстанцирования.
Следующий бит [temp.dep.candidate]/1
:
Для вызова функции, где постфиксное выражение является зависимым именем, функции-кандидаты обнаруживаются с использованием обычных правил поиска (3.4.1, 3.4.2), за исключением того, что:
- Для части поиска, использующей поиск неквалифицированных имен (3.4.1), найдены только объявления функций из контекста определения шаблона.
- Для части поиска с использованием связанных пространств имен (3.4.2) найдены только объявления функций, найденные в контексте определения шаблона или контекста создания шаблона.
Последние две части - это "две фазы" двухфазного поиска. (Примечание - этот раздел изменен в формулировке от С++ 11 до С++ 14, но эффект в том же).
В первой фазе, 3.4.1, имена foobar
не найдены.
Итак, мы переходим на вторую фазу. Фактические места, названия которых выглядят, как описано в 3.4.2. Текст длинный, но вот два из соответствующих правил:
Если T является фундаментальным типом, его ассоциированные множества пространств имен и классов являются пустыми.
Если T - тип класса (включая объединения), его ассоциированные классы: сам класс; класс которого он является членом, если таковой имеется; и его прямые и косвенные базовые классы. Его связанные пространства имен являются внутренними охватывающими пространствами имен связанных классов. [...]
Итак, когда вы создаете экземпляр Foo<int>
, тогда вторая фаза поиска не вводит никаких дополнительных пространств имен для поиска.
Однако, если вы измените свой пример на struct X {};
, а затем измените int
на X
всюду, тогда код скомпилируется. Это связано с последней точкой маркера: ADL для аргумента типа класса выполняет поиск охватывающего пространства имен этого класса (который теперь является глобальным пространством имен), однако ADL для аргумента встроенного типа не ищет глобальное пространство имен.