Как компилятор находит шаблонную функцию X::max(T const&, T const&) через ADL в приведенном ниже коде?

Цитата из стандарта приветствуется.

#include <iostream>

namespace X {
    class A {};
}

template <typename T>
inline T const& max(T const& a, T const& b, T const& c)
{
    return max(max(a, b), c);
}

inline X::A const& max(X::A const& a, X::A const& b)
{
    std::cout << "non-template" << '\n';
    return a;
}

int main()
{
    X::A a, b, c;
    max(a, b, c);
}

namespace X {
    template <typename T>
    inline T const& max(T const& a, T const& b)
    {
        std::cout << "template" << '\n';
        return a;
    }
}

Живой пример


person Wake up Brazil    schedule 02.07.2014    source источник


Ответы (1)


Стандартный

Вызов max() в примере влечет за собой зависимое имя, поскольку его аргументы зависят от параметра шаблона T. Двухэтапный поиск таких зависимых имен определен в Стандарте следующим образом:

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), которые иначе не видны. Эти модификации поиска зависят от типов аргументов (а для аргументов шаблона шаблона — от пространства имен аргумента шаблона).

Почему ваш код не работает

В вашем примере и неквалифицированный поиск, и ADL не находят никакой перегрузки в точке определения, потому что компилятор еще не видел никакого двухаргументного max(). ADL также применяется в момент создания экземпляра, и в это время компилятор видит template max(T, T) с двумя аргументами, который является единственным, который можно вызвать. (разница в том, что экземпляр шаблона происходит после того, как вся единица перевода была проанализирована).

Лучший код

Вы должны исправить свой код, поместив перегрузку max(X::A, X::A) без шаблона внутрь namespace X и переместив template max(T, T) из него.

#include <iostream>

// generic code

template <typename T>
inline T const& max(T const& a, T const& b)
{
    std::cout << "template" << '\n';
    return a;
}

template <typename T>
inline T const& max(T const& a, T const& b, T const& c)
{
    using ::max; // fallback if no user-defined max
    return max(max(a, b), c);
}

// X specific code

namespace X {

class A {};

inline X::A const& max(X::A const& a, X::A const& b)
{
    std::cout << "non-template" << '\n';
    return a;
}

} // namespace X

int main()
{
    X::A a, b, c;
    max(a, b, c);
}

Live-Example, который дважды печатает "не шаблон".

person TemplateRex    schedule 02.07.2014
comment
Кстати, компиляторы, которые неправильно реализуют двухфазный поиск, могут вместо этого вызвать non-template версию max (например, Visual C++). - person ComicSansMS; 02.07.2014
comment
@ComicSansMS на платформах Win может быть даже макрос max, который мешает ему, поэтому вам нужно добавить дополнительные скобки (max)(a,b) к вызову. - person TemplateRex; 02.07.2014
comment
И (max) блокирует ADL. (одно из немногих мест, где () в выражении меняет значение выражения) - person Yakk - Adam Nevraumont; 02.07.2014
comment
@TemplateRex and at that time, the compiler has seen the two-argument template max(T, T) - именно этого я не понимаю. - person Wake up Brazil; 02.07.2014
comment
@WakeupBrazil компилятор анализирует всю единицу перевода перед созданием экземпляра любого шаблона. Вот почему 2-й ADL находит максимальное значение шаблона, а неквалифицированный и 1-й ADL — нет. ОБНОВЛЕНО. - person TemplateRex; 02.07.2014
comment
@TemplateRex Это может быть ответ, который я ищу. Знаете ли вы какие-либо цитаты в Стандарте, подтверждающие это? - person Wake up Brazil; 02.07.2014
comment
@Yakk, по иронии судьбы, защита от макросов Windows max теоретически блокирует ADL, но поскольку ADL все равно не работает .. ;-) - person TemplateRex; 02.07.2014
comment
@TemplateRex Комментарий Вандервуда и Джосуттиса, который вы разместили здесь ( stackoverflow.com/q/23030403/2548699 ), похоже, указывает на то, что только невстроенные шаблоны создаются в конце единицы перевода. В моем примере выше все функции шаблона являются встроенными. Не могли бы вы прокомментировать это? - person Wake up Brazil; 03.07.2014
comment
@WakeupBrazil, если вы посмотрите на ответ Кейси в этом вопросе и ответе, вы увидите, что конец перевода добавляется как точка создания экземпляра (POI) и что шаблоны (независимо от того, являются ли они явно встроенными или нет) могут иметь несколько POI. Вам просто нужно быть осторожным с ODR-нарушениями. - person TemplateRex; 03.07.2014