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

85

Какая часть спецификации 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.
}
Хью
источник
Означает ли это, что вы рассчитываете работать с frob () без написания ns :: frob ()?
Саймон
Да, в виде нешаблонной функции.
Хью
К вашему сведению, приведенный выше код также не работает в Comeau: comeaucomputing.com/tryitout - добавление using namespace ns;или ns::компиляция квалификации проходит. Это хороший вопрос.
fbrereto 01
3
@Huw: просто укусил это :) Забавно, насколько явные квалификационные правила ADL, я думаю: /
Matthieu M.
1
@Matt: Ха-ха, и я сейчас тоже. Маленький мир программирования.
GManNickG

Ответы:

88

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

Стандарт 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
}
Корнель Киселевич
источник
9
В чем причина этого? Странное требование. Я имею в виду, при чем здесь синтаксическая форма?
Lightness Races in Orbit
22
@LightnessRacesinOrbit Раздел 9.3.5 в Vandevoorde & Josuttis объясняет , почему это является синтаксической проблемой (именования приняты на пример OP в): «компилятор не может решить , что f<3>(b)является аргументом вызова функции , пока не решил , что <3>список аргументов шаблона С другой стороны , мы. не может решить, что <3>это список аргументов шаблона, пока мы не найдем f()шаблон. Поскольку эта проблема с курицей и яйцом не может быть решена, выражение анализируется как (f<3)>(b), что не имеет смысла. " Обратите внимание, что это похоже на templateсинтаксис разрешения неоднозначности для шаблонов функций-членов.
TemplateRex
9
Есть ли предложения по устранению этой проблемы? template f<3>(b)может быть лучше синтаксис?
balki 05
1
Выражения ( выражения @AngelusMortis ...) (обратите внимание на отсутствие оператора перед скобкой) всегда является вызовом функции, и компилятор знает это, как только видит открывающую скобку. Проблема здесь в том, что он <может служить как оператором, так и началом списка аргументов шаблона, и компилятор должен провести много дополнительного синтаксического анализа, чтобы выяснить, какие (и, возможно, есть некоторые схемы кода, где это невозможно делать однозначно). Похоже, что авторы стандарта решили сделать это незаконным, возможно, чтобы спасти разработчиков компиляторов.
Miral
3
Это требование было снято в C ++ 20, и код OP теперь правильно сформирован :)
Rakete1111,
8

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

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

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

live demo.

陳 力
источник
5

Я хотел бы уточнить слегка принятый ответ. Это неясно в вопросе 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);
}
Марцинь
источник
0

Изменить: нет, это неправильно. См . Ответ @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*/ }
}
Трубадур
источник
1
Нет, возьмите пространства имен, и у вас все еще есть проблема, не так ли? Специализация после использования - обычная проблема в C ++, специализированная форма не используется, если объявлена ​​после.
Kornel Kisielewicz
@Kornel: Ах да, это дает другую ошибку, еще одну в соответствии с тем, что я описал. Достаточно честно, спасибо, что указали на это.
Трубадур