Преамбула с большим количеством стандартного языка
Вызов swap()
в этом примере влечет за собой зависимое имя, потому что его аргументы begin[0]
и begin[1]
зависят от параметра шаблона T
окружающего algorithm()
шаблона функции. Двухэтапный поиск таких зависимых имен определяется в Стандарте следующим образом:
14.6.4.2 Функции-кандидаты [temp.dep.candidate]
1 Для вызова функции, где постфиксное выражение является зависимым именем, функции-кандидаты находятся с использованием обычных правил поиска (3.4.1, 3.4.2), за исключением того, что:
- Для части поиска, использующей поиск по некорректному имени (3.4.1), обнаруживаются только объявления функций из контекста определения шаблона.
- Для части поиска с использованием связанных пространств имен (3.4.2) обнаруживаются только объявления функций, найденные либо в контексте определения шаблона, либо в контексте создания экземпляра шаблона.
Неквалифицированный поиск определяется
3.4.1 Неправильный поиск имени [basic.lookup.unqual]
1 Во всех случаях, перечисленных в п. 3.4.1, в областях производится поиск декларации в порядке, указанном в каждой из соответствующих категорий; поиск имени заканчивается, как только будет найдено объявление для имени. Если объявление не найдено, программа имеет неправильный формат.
и поиск, зависящий от аргументов (ADL) как
3.4.2 Поиск имени в зависимости от аргумента [basic.lookup.argdep]
1 Когда постфиксное-выражение в вызове функции (5.2.2) является неквалифицированным-идентификатором, можно искать другие пространства имен, не учитываемые при обычном неквалифицированном поиске (3.4.1), и в этих пространствах имен могут быть найдены объявления дружественной функции области пространства имен или шаблона функции (11.3), не видимые иным образом. Эти изменения в поиске зависят от типов аргументов (а для аргументов шаблона шаблона - пространства имен аргумента шаблона).
Применение Стандарта к примеру
Первый пример вызывает exp::swap()
. Это не зависимое имя и не требует двухэтапного поиска имени. Поскольку вызов swap квалифицирован, выполняется обычный поиск, который находит только общий шаблон функции swap(T&, T&)
.
Второй пример (то, что @HowardHinnant называет «современным решением») вызывает swap()
, а также имеет перегрузку swap(A&, A&)
в том же пространстве имен, что и где находится class A
(в данном случае глобальное пространство имен). Поскольку вызов подкачки неквалифицирован, и обычный поиск, и ADL имеют место в точке определения (опять же, только поиск общего swap(T&, T&)
), но другой ADL имеет место в точке создания экземпляра (то есть там, где exp::algorithm()
вызывается в main()
), и это поднимает swap(A&, A&)
, что лучше соответствует разрешению перегрузки.
Все идет нормально. Теперь переход на бис: третий пример вызывает swap()
и имеет специализацию template<> swap(A&, A&)
внутри namespace exp
. Поиск такой же, как во втором примере, но теперь ADL не выбирает специализацию шаблона, потому что он не находится в связанном пространстве имен class A
. Однако даже несмотря на то, что специализация template<> swap(A&, A&)
не играет роли при разрешении перегрузки, она все равно создается в точке использования.
Наконец, четвертый пример вызывает swap()
и содержит перегрузку template<class T> swap(A<T>&, A<T>&)
внутри namespace exp
для template<class T> class A
, живущего в глобальном пространстве имен. Поиск такой же, как в третьем примере, и снова ADL не принимает перегрузку swap(A<T>&, A<T>&)
, потому что она не находится в связанном пространстве имен шаблона класса A<T>
. И в этом случае также нет специализации, которая должна быть создана в точке использования, поэтому здесь вызывается общий swap(T&, T&)
.
Вывод
Даже если вам не разрешено добавлять новые перегрузки к namespace std
и только явные специализации, это даже не сработает из-за различных сложностей двухэтапного поиска имени.
person
TemplateRex
schedule
27.01.2014
swap
, и ADL не находит ее, потому что нет связанного пространства именstd
для запуска поиска в это пространство имен). (Это слишком кратко, чтобы быть полным ответом, но может помочь вам начать понимание.) - person   schedule 27.01.2014swap
вnamespace std
: это неопределенное поведение. Первый фрагмент кода связанного ответа исследует, что произошло бы, если он определил поведение и действовал так же, как и любой другойnamespace
, используя другойnamespace
(exp
для экспериментального). - person Yakk - Adam Nevraumont   schedule 27.01.2014namespace std
(хотя это не так - по причинам, не связанным с вопросом). Я мог бы использоватьnamespace exp
в своем вопросе, но почему-то думаю, что это не привлечет такого же (надеюсь, должного) внимания. - person Dan Nissenbaum   schedule 27.01.2014swap
на классе шаблона, а не на перегрузке? Это вообще возможно? - person Dan Nissenbaum   schedule 27.01.2014swap
, но ее специализации для класса template? - person Dan Nissenbaum   schedule 27.01.2014exp::algorithm
не будет создан компилятором до тех пор, пока не будет скомпилирована функция main, в это время перегруженная версияswap
находится - в чем я ошибаюсь? Спасибо. - person Dan Nissenbaum   schedule 27.01.2014