Dlaczego ADL nie znajduje szablonów funkcji?


85

Która część specyfikacji C ++ ogranicza wyszukiwanie zależne od argumentów od znajdowania szablonów funkcji w zestawie powiązanych przestrzeni nazw? Innymi słowy, dlaczego ostatnie wywołanie mainponiżej nie daje się skompilować?

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.
}

Czy to oznacza, że ​​spodziewasz się pracy frob () bez wpisywania ns :: frob ()?
Simon

Tak, na zasadzie funkcji innej niż szablonowa.
Hugh

FYI, powyższy kod również zawodzi w Comeau: comeaucomputing.com/tryitout - dodanie using namespace ns;lub ns::kompilacja przejść kwalifikacyjnych. To jest dobre pytanie.
fbrereto

3
@Huw: po prostu ugryziony :) Zabawne, jak jasne są zasady kwalifikacji ADL, chyba: /
Matthieu M.

1
@Matt: Haha, i ja też teraz. Mały świat programowania.
GManNickG

Odpowiedzi:


88

Ta część wyjaśnia to:

Standard C ++ 03 14.8.1.6 :

[Uwaga: W przypadku prostych nazw funkcji wyszukiwanie zależne od argumentów (3.4.2) ma zastosowanie nawet wtedy, gdy nazwa funkcji nie jest widoczna w zakresie wywołania. Dzieje się tak, ponieważ wywołanie nadal ma postać składniową wywołania funkcji (3.4.1). Ale gdy używany jest szablon funkcji z jawnymi argumentami szablonu, wywołanie nie ma poprawnej formy składniowej, chyba że istnieje szablon funkcji o tej nazwie widoczny w miejscu wywołania. Jeśli żadna taka nazwa nie jest widoczna, wywołanie nie jest poprawnie sformułowane składniowo, a wyszukiwanie zależne od argumentów nie ma zastosowania. Jeśli jakaś taka nazwa jest widoczna, ma zastosowanie wyszukiwanie zależne od argumentów, a dodatkowe szablony funkcji można znaleźć w innych przestrzeniach nazw.

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
Jakie jest tego uzasadnienie? Wydaje się, że to dziwny wymóg. Mam na myśli, co ma wspólnego forma składniowa z czymkolwiek?
Wyścigi lekkości na orbicie

22
@LightnessRacesinOrbit Sekcja 9.3.5 w Vandevoorde & Josuttis wyjaśnia, dlaczego jest to problem składniowy (nazewnictwo przyjęte na przykładzie OP): „kompilator nie może zdecydować, że f<3>(b)jest to argument wywołania funkcji, dopóki nie zdecyduje, że <3>jest to lista argumentów szablonu. nie możemy zdecydować, że <3>jest to lista argumentów szablonu, dopóki nie znajdziemy f()szablonu. Ponieważ tego problemu z kura i jajkiem nie można rozwiązać, wyrażenie jest przetwarzane jako (f<3)>(b), co nie ma sensu. " Zauważ, że jest to podobne do templateskładni ujednoznaczniania dla szablonów funkcji składowych.
TemplateRex,

9
Czy są jakieś propozycje rozwiązania tego problemu? template f<3>(b)może być lepszą składnią?
balki 05.06.15

1
Wyrażenia @AngelusMortis ( ... ) (zwróć uwagę na brak operatora przed nawiasem) są zawsze wywołaniem funkcji i kompilator wie o tym, gdy tylko zobaczy otwarty nawias. Problem polega na tym, że <może służyć zarówno jako operator, jak i początek listy argumentów szablonu, a kompilator musi wykonać wiele dodatkowej analizy, aby dowiedzieć się, które (i prawdopodobnie istnieją pewne ustalenia kodu, w których nie jest to możliwe zrobić jednoznacznie). Wygląda na to, że autorzy standardu postanowili uczynić to nielegalnym, być może po to, by oszczędzić włos twórcom kompilatorów.
Miral

3
To wymaganie zostało zniesione w C ++ 20, a kod OP jest teraz dobrze sformułowany :)
Rakete1111

8

Od C ++ 20 adl działa również dobrze z jawnym szablonem funkcji. Oto propozycja: P0846R0: Szablony ADL i funkcji, które nie są widoczne :

Zamiast wymagać od użytkownika użycia słowa kluczowego szablonu, zaproponowano zmianę reguł wyszukiwania, tak aby nazwa, dla której normalne wyszukiwanie nie dawała żadnego wyniku lub znajdowała jedną lub więcej funkcji, po której następuje aa „<”, byłaby traktowana jako jeśli nazwa szablonu funkcji została znaleziona i spowodowałaby wykonanie ADL.

Obecnie tylko GCC 9 ma zaimplementowaną tę funkcję, więc Twój przykład może się kompilować.

live demo.


5

Chciałbym zawęzić lekko zaakceptowaną odpowiedź. Nie jest to jasne w pytaniu PO, ale istotna część standardu (cytowana przez Kornela) jest taka (wyróżnienie moje):

Ale gdy używany jest szablon funkcji z jawnymi argumentami szablonu , wywołanie nie ma poprawnej formy składniowej

więc to, co jest zabronione, to poleganie na ADL i używanie jawnych argumentów szablonów. Niestety użycie argumentów szablonu innego niż typ wymaga użycia jawnych argumentów (chyba że mają wartości domyślne).

Poniżej znajduje się przykładowy kod pokazujący to:

[relacja na żywo]

#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

Edycja: Nie, to nie jest w porządku. Zobacz odpowiedź @ Kornel .


Nie jestem do końca pewien, ale po konsultacji z „Językiem programowania C ++” Stroustrupa myślę, że przyczyną może być sekcja 13.8.4 dodatku C.

Ponieważ frobjest to szablon, można by go wyspecjalizować i=0w momencie, gdy go wywołasz. Oznacza to, że implementacji pozostałyby dwa możliwe sposoby wyboru frobwywołania, ponieważ wydaje się, że może to wybrać w momencie tworzenia instancji lub na końcu przetwarzania jednostki tłumaczącej.

Więc myślę, że problem polega na tym, że możesz to zrobić

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
Nie, pozbądź się przestrzeni nazw i nadal masz problem, prawda? Specjalizacja po użyciu jest normalnym problemem w C ++, wyspecjalizowany formularz nie jest używany, jeśli został zadeklarowany po.
Kornel Kisielewicz

@Kornel: Ach tak, to daje inny błąd, jeszcze jeden zgodny z tym, co opisałem. W porządku, dzięki za zwrócenie uwagi.
Troubadour
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.