Разрешение перегрузки, глядя в пространства имен

Следующий код завершается неудачно, как и ожидалось, поскольку перегрузка get не найдена. Использование std::get решит проблему.

#include <array>

int main()
{
    std::array<int, 2> ar{2,3};
    auto r = get<0>(ar);//fails, get was not declared in this scope
}

Однако введение шаблонной версии get, даже если она не соответствует вызову функции, каким-то образом заставляет компилятор использовать версию std::get:

#include <array>

template <typename T>
void get(){};

int main()
{
    std::array<int, 2> ar{2,3};

    auto r = get<0>(ar);//returns 2
}

Я не могу найти какую-либо часть стандарта, которая объясняет это. Это ошибка во всех 3-х компиляторах, которые я тестировал (вероятно, нет), или я что-то упустил?

Это поведение было проверено в

  • MSVC 15.9.2
  • Лязг 8.0.0
  • GCC 9.0.0 (все еще экспериментальная версия)

РЕДАКТИРОВАТЬ: я знаю о ADL. Но если ADL заставляет работать второй код, почему он не работает в первой части?

Ответ 1

ADL не используется, когда задействованы явные аргументы шаблона, если вы не введете объявление функции шаблона в точке вызова. Вы используете неквалифицированную форму get с использованием нетипичного аргумента шаблона 0, поэтому вам нужно ввести объявление функции шаблона или использовать квалифицированную версию get как std::get<0>(ar).

В стандарте [temp.arg.explicit]/8: (выделение мое)

[Примечание: для простых имен функций, зависящий от аргумента поиск (6.4.2) применяется, даже если имя функции не отображается в рамках вызова. Это связано с тем, что вызов по-прежнему имеет синтаксическую форму вызова функции (6.4.1). Но когда используется шаблон функции с явными аргументами шаблона, вызов не имеет правильной синтаксической формы, если в точке вызова не отображается шаблон функции с таким именем. Если такого имени не видно, вызов не является синтаксически правильно сформированным, и поиск, зависящий от аргумента, не применяется. Если какое-то такое имя является видимым, применяется поиск, зависящий от аргумента, и дополнительные шаблоны функций могут быть найдены в других пространствах имен.

РЕДАКТИРОВАТЬ:

Как указал в комментарии @Yakk - Адам Невраумонт, без присутствия объявления функции шаблона выражение get<0>(ar) будет проанализировано как (get<0)>(ar), то есть как серия выражения сравнения вместо вызова функции.

Ответ 2

Обратите внимание, что это изменилось в С++ 20 в результате P0846R0. Неквалифицированное имя, за которым следует токен < для которого обычный неквалифицированный поиск либо находит одну или несколько функций, либо ничего не находит, теперь предполагается, что он присваивает имя шаблону, а символ < анализируется соответствующим образом.