Почему ADL не находит шаблоны функций? - PullRequest
79 голосов
/ 02 июня 2010

Какая часть спецификации 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 ]

82 голосов
/ 02 июня 2010

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

C ++ Стандарт 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
}
4 голосов
/ 30 августа 2016

Хотелось бы уточнить слегка принятый ответ. Это не ясно в вопросе 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);
}
2 голосов
/ 30 декабря 2018

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

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

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

live demo.

0 голосов
/ 02 июня 2010

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


Я не совсем уверен, но, посмотрев "Страуструп" "Язык программирования 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*/ }
}
...