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

person Hugh    schedule 01.06.2010    source источник
comment
Означает ли это, что вы ожидаете, что frob() будет работать без написания ns::frob()?   -  person Simon    schedule 02.06.2010
comment
Да, как нешаблонная функция.   -  person Hugh    schedule 02.06.2010
comment
К вашему сведению, приведенный выше код также не работает в Comeau: comeaucomputing.com/tryitout — добавление using namespace ns; или ns:: квалификация проходит компиляцию. Это хороший вопрос.   -  person fbrereto    schedule 02.06.2010
comment
Еще одна точка данных: для template<typename T> void bar(T) {} ADL работает нормально, bar(f) успешно.   -  person sbi    schedule 02.06.2010
comment
@Huw: просто укусил это :) Забавно, как явная квалификация исключает ADL, я думаю:/   -  person Matthieu M.    schedule 24.01.2011
comment
@Matt: Ха-ха, и я тоже только что. Маленький мир программирования.   -  person GManNickG    schedule 26.01.2011
comment
Теперь он работает в C++20; благодаря P0846.   -  person user2023370    schedule 20.01.2021


Ответы (4)


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

Стандарт 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
}
person Kornel Kisielewicz    schedule 01.06.2010
comment
Ах, я не мог найти эту ссылку :) +1 - person Georg Fritzsche; 02.06.2010
comment
Да, это заметка, поэтому ее нелегко найти :/ - person Kornel Kisielewicz; 02.06.2010
comment
В чем причина этого? Кажется странным требованием. Я имею в виду, при чем здесь синтаксическая форма? - person Lightness Races in Orbit; 12.11.2012
comment
@LightnessRacesinOrbit Раздел 9.3.5 в Vandevoorde & Josuttis объясняет, почему это является синтаксической проблемой (именование адаптировано к примеру OP): компилятор не может решить, что f<3>(b) является аргументом вызова функции, пока не решит, что <3> является список аргументов шаблона. И наоборот, мы не можем решить, что <3> является списком аргументов шаблона, пока мы не обнаружим, что f() является шаблоном. Поскольку эту проблему с курицей и яйцом решить невозможно, выражение анализируется как (f<3)>(b), что не имеет смысла. Обратите внимание, что это похоже на синтаксис устранения неоднозначности template для шаблонов функций-членов. - person TemplateRex; 17.11.2013
comment
Есть предложения по устранению этой проблемы? template f<3>(b) может быть лучшим синтаксисом? - person balki; 05.06.2015
comment
@TemplateRex В коде ОП, где строку: non_template(f); можно опровергнуть той же теорией, что и в вашем комментарии, т.е. a compiler cannot decide that "non_template_function(b)" is a function call, until it's decided "b" is function call argument, which can't be decided until we have found non_template_function(). Because of this chicken and egg problem call shouldn't have resolved... - person Angelus Mortis; 23.01.2016
comment
@AngelusMortis выражение ( выражения... ) (обратите внимание на отсутствие оператора перед скобкой) всегда является вызовом функции, и компилятор знает, что, как только он увидит открытая скобка. Проблема здесь в том, что < может служить как оператором, так и началом списка аргументов шаблона, и компилятору приходится выполнять много дополнительных анализов, чтобы выяснить, какие (и, возможно, есть некоторые компоновки кода, где это невозможно сделать однозначно). Похоже, что авторы стандарта решили сделать это незаконным, возможно, чтобы сохранить волосы разработчикам компиляторов. - person Miral; 01.05.2017
comment
Это требование было снято в С++ 20, и код OP теперь имеет правильный формат :) - person Rakete1111; 28.07.2018

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

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

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

live demo.

person 陳 力    schedule 30.12.2018
comment
Превосходно! Сейчас работаю в Clang и других местах. - person user2023370; 20.01.2021

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

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

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

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

[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);
}
person marcinj    schedule 30.08.2016

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


Я не совсем уверен, но, ознакомившись с «Языком программирования 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*/ }
}
person Troubadour    schedule 01.06.2010
comment
Нет, возьмите пространства имен, и у вас все еще есть проблема, не так ли? Специализация после использования — обычная проблема в C++, специализированная форма не используется, если она объявлена ​​после. - person Kornel Kisielewicz; 02.06.2010
comment
@Kornel: Ах да, это дает другую ошибку, еще одну в соответствии с тем, что я описал. Достаточно справедливо, спасибо, что указали на это. - person Troubadour; 02.06.2010