Подтвердить что ты не робот

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

Какая часть спецификации С++ ограничивает поиск зависимых от аргументов зависимостей от поиска шаблонов функций в наборе связанных пространств имен? Другими словами, почему последний вызов в 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.
}
4b9b3361

Ответ 1

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

С++ Standard 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
}

Ответ 2

Я хотел бы уточнить слегка принятый ответ. В вопросе ОП неясно, но важная часть из стандарта (цитируется Корнелом) заключается в этом (внимание мое):

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

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

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

[live]

#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);
}

Ответ 3

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

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

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

live demo.

Ответ 4

Изменить: Нет, это неправильно. См. @Kornel answer.


Я не совсем уверен, но, консультируясь с Stroustrup "Язык программирования на С++", я думаю, что причиной этого может стать раздел 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*/ }
}