Чому ADL не знаходить шаблонів функцій?


83

Яка частина специфікації 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 ()?
Саймон

Так, на манер нешаблонної функції.
Х'ю,

FYI, зазначений вище код також зазнає помилки в Comeau: comeaucomputing.com/tryitout - додавання using namespace ns;або ns::кваліфікація проходить компіляцію. Це гарне запитання.
fbrereto

3
@Huw: просто покусав це :) Кумедно, як явно виходять правила кваліфікації ADL Я здогадуюсь: /
Matthieu M.

1
@Matt: Ха-ха, і я теж щойно. Невеликий світ програмування.
GManNickG

Відповіді:


87

Ця частина пояснює це:

C ++ Standard 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
Яке обґрунтування цього? Виглядає дивною вимогою. Я маю на увазі, яке відношення до чого-небудь має синтаксична форма?
Гонки легкості на орбіті

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

1
Вирази виразу @AngelusMortis ( ... ) (зверніть увагу на відсутність оператора перед дужкою), однак, завжди є викликом функції, і компілятор знає це, як тільки побачить відкриту дужку. Проблема тут полягає в тому, що він <може служити і оператором, і початком списку аргументів шаблону, і компілятору доводиться робити багато додаткового аналізу, щоб з'ясувати, який (і, можливо, є деякі механізми коду, де це неможливо) робити однозначно). Здається, стандартні автори обрали зробити це незаконним, можливо, щоб зберегти волосся розробників компіляторів.
Мірал,

2
Цю вимогу було скасовано в C ++ 20, і код OP тепер добре сформований :)
Rakete1111

8

Оскільки c ++ 20, adl також чудово працює з явним шаблоном функції. Ось пропозиція: P0846R0: ADL та шаблони функцій, які не відображаються :

Замість того, щоб вимагати від користувача використання ключового слова шаблону, було запропоновано перегляд правил пошуку, щоб ім'я, для якого звичайний пошук не давав результату, або знаходило одну або кілька функцій, і за яким слідує aa "<", розглядалося як якби було знайдено ім'я шаблону функції та спричинило б виконання 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 ++" Stroustrup, я думаю, що причиною може бути розділ 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: Так, це дає іншу помилку, ще одну, що відповідає тому, що я описав. Досить справедливо, дякую, що вказали на це.
Трубадур,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.