Причиной сбоя является то, что при создании экземпляра не выполняется поиск безоговорочного имени для функций (но только ADL - Аргумент-зависимый поиск). Контекст создания экземпляра (взят из 14.6.4.1/6
стандарта C ++):
Контекст экземпляра выражения, который зависит от аргументов шаблона, представляет собой набор объявлений с внешней связью, объявленной до момента создания специализации шаблона в той же единице перевода.
Точка создания всех тех специализаций шаблонов, которые вы вызвали в этом случае, находится сразу после определения test
(читай 14.6.4.1/1
). Итак, все объявленные вами функции видны в вашей функции test
с использованием неквалифицированного поиска, но поиск для них на самом деле отличается для вызовов функций:
Вызов функции, который зависит от параметра шаблона в шаблоне, выглядит следующим образом:
- Имена из контекста определения шаблона рассматриваются как обычным поиском, так и ADL.
- Имена из контекста экземпляра рассматриваются только для ADL.
Это означает, что, поскольку в контексте определения шаблона нет подходящей функции getClassName
, подходящая функция должна быть найдена в контексте создания экземпляра с использованием ADL - в противном случае вызов завершится неудачно и не найдет никакого объявления.
Зависимый от аргумента поиск (ADL)
Для аргумента типа std::vector<T>
ADL ищет функции в пространстве имен std
и пространстве имен T
. Помещение функции getClassName
в пространство имен std
будет работать для этого (но это не разрешено Стандартом, потому что это приводит к неопределенному поведению - это должно быть сделано только в качестве последнего средства).
Чтобы увидеть эффекты ADL
, попробуйте позвонить doIt
с вектором MyClass2
вместо int
. С тех пор T = MyClass2
, ADL будет искать в пространстве имен MyClass2
подходящую функцию, принимающую std::vector<MyClass2>
, и преуспеет - в отличие от того, когда вы используете int
, которая будет смотреть только на std
.
Для других вызовов функций все их соответствующие объявления также найдены, поскольку все они объявлены в глобальном пространстве имен, в котором также определены типы аргументов вызовов функций (MyClass1
, MyClass2
и т. Д.).
FAQ по C ++ - это хорошо, но он не углубляется в шаблоны (в нем не упоминается ADL). Существует специальный шаблон faq , который обрабатывает некоторые из наиболее сложных ловушек.
Остерегайтесь неопределенного поведения
Обратите внимание, что многие компиляторы будут принимать код, даже когда вы поместите эту декларацию, которую я показал после функции test
(вместо нее). Но, как сказано в приведенной выше стандартной цитате, объявление не будет частью контекста создания экземпляра, и следует соблюдать правило, найденное в 14.6.4.2/1
:
Если вызов будет некорректным или найдет лучшее совпадение, при поиске в связанных пространствах имен будут рассмотрены все объявления функций с внешней связью, введенные в эти пространства имен во всех единицах перевода, а не только те объявления, которые найдены в шаблоне контексты определения и создания шаблона, затем программа имеет неопределенное поведение.
Таким образом, то, что будет работать, будет неопределенным поведением. Это допустимо для компилятора, чтобы принять его, но также справедливо и для того, чтобы он отклонил его или аварийно завершил работу. Так что следите за тем, чтобы любое нужное имя действительно было видно в контексте реализации, как объяснено.
Надеюсь, это поможет.