Какая часть спецификации 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.
}
using namespace ns;
илиns::
компиляция квалификации проходит. Это хороший вопрос.Ответы:
Эта часть объясняет это:
Стандарт C ++ 03 14.8.1.6 :
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 }
источник
f<3>(b)
является аргументом вызова функции , пока не решил , что<3>
список аргументов шаблона С другой стороны , мы. не может решить, что<3>
это список аргументов шаблона, пока мы не найдемf()
шаблон. Поскольку эта проблема с курицей и яйцом не может быть решена, выражение анализируется как(f<3)>(b)
, что не имеет смысла. " Обратите внимание, что это похоже наtemplate
синтаксис разрешения неоднозначности для шаблонов функций-членов.template f<3>(b)
может быть лучше синтаксис?(
выражения @AngelusMortis ...)
(обратите внимание на отсутствие оператора перед скобкой) всегда является вызовом функции, и компилятор знает это, как только видит открывающую скобку. Проблема здесь в том, что он<
может служить как оператором, так и началом списка аргументов шаблона, и компилятор должен провести много дополнительного синтаксического анализа, чтобы выяснить, какие (и, возможно, есть некоторые схемы кода, где это невозможно делать однозначно). Похоже, что авторы стандарта решили сделать это незаконным, возможно, чтобы спасти разработчиков компиляторов.Начиная с C ++ 20, adl также отлично работает с явным шаблоном функции. Вот предложение: P0846R0: ADL и шаблоны функций, которые не видны :
В настоящее время только GCC 9 реализует эту функцию, поэтому ваш пример можно компилировать.
live demo
.источник
Я хотел бы уточнить слегка принятый ответ. Это неясно в вопросе OP, но важная часть стандарта (цитируется Корнелем) заключается в следующем (выделено мной):
поэтому запрещено полагаться на 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*/ } }
источник