Почему ADL не находит шаблоны функций?

Какая часть спецификации C++ ограничивает поиск, зависящий от аргументов, от поиска шаблонов функций в наборе связанных пространств имен? Другими словами, почему последний звонок в main ниже не скомпилировать?

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
    void non_template(foo const&) {}
}

int main() {
    ns::foo f;
    non_template(f); // This is fine.
    frob<0>(f); // This is not.
}

4 ответа

Решение

Эта часть объясняет это:

Стандарт С ++ 03 14.8.1.6:

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

namespace A {
  struct B { };
  template<int X> void f(B);
}
namespace C {
  template<class T> void f(T t);
}
void g(A::B b) {
  f<3>(b);    //ill-formed: not a function call
  A::f<3>(b); //well-formed
  C::f<3>(b); //ill-formed; argument dependent lookup
              // applies only to unqualified names
  using C::f;
  f<3>(b);    //well-formed because C::f is visible; then
              // A::f is found by argument dependent lookup
}

Начиная с C++20, adl также отлично работает с явным шаблоном функции. Вот предложение: P0846R0: ADL и шаблоны функций, которые не видны:

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

В настоящее время только GCC 9 имеет эту функцию, поэтому ваш пример может быть скомпилирован.

live demo,

Я хотел бы уточнить слегка принятый ответ. Это не ясно в вопросе OP, но важная часть стандарта (цитируемая Kornel) заключается в следующем (выделено мной):

Но когда используется шаблон функции с явными аргументами шаблона, вызов не имеет правильной синтаксической формы

поэтому запрещено полагаться на ADL и использовать явные аргументы шаблона. К сожалению, использование не типовых аргументов шаблона требует использования явных аргументов (если они не имеют значений по умолчанию).

Ниже приведен пример кода, показывающего это:

[жить]

#include <string>
#include <utility>

namespace C {
  struct B { };
  template<class T> void f(T t){}
}

void g(C::B b) {
  f(b);           // OK
  //f<C::B>(b);   // ill-formed: not a function call, but only 
                  //  because explicit template argument were used

  std::string s;
  move(s);                      // OK
  //move<std::string&>(s);      // Error, again because 
                                //  explicit template argument were used
  std::move<std::string&>(s);   // Ok
}

int main()
{
 C::B b;
 g(b);
}

Редактировать: нет, это не правильно. Смотрите ответ @Kornel.


Я не совсем уверен, но, посмотрев на Страуструпа "Язык программирования C++", я думаю, что причиной может быть раздел 13.8.4 Приложения C.

поскольку frob это шаблон, для которого можно было бы специализировать его i=0 в момент после того, как вы это называете. Это означает, что реализация будет оставлена ​​с двумя возможными способами выбора, которые frob чтобы вызвать, как кажется, он может выбрать его в момент создания или в конце обработки единицы перевода.

Итак, я думаю, проблема в том, что ты мог бы сделать

namespace ns {
    struct foo {};
    template<int i> void frob(foo const&) {}
}

int main() {
    ns::foo f;
    frob<0>(f);
    return 0;
}

namespace ns {
    template<> void frob< 0 >(foo const&) { /* Do something different*/ }
}
Другие вопросы по тегам