Почему 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*/ }
}