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 main
poniż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.
}
using namespace ns;
lubns::
kompilacja przejść kwalifikacyjnych. To jest dobre pytanie.Odpowiedzi:
Ta część wyjaśnia to:
Standard 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 }
źródło
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 znajdziemyf()
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 dotemplate
składni ujednoznaczniania dla szablonów funkcji składowych.template f<3>(b)
może być lepszą składnią?(
...)
(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.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 :
Obecnie tylko GCC 9 ma zaimplementowaną tę funkcję, więc Twój przykład może się kompilować.
live demo
.źródło
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):
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); }
źródło
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ż
frob
jest to szablon, można by go wyspecjalizowaći=0
w momencie, gdy go wywołasz. Oznacza to, że implementacji pozostałyby dwa możliwe sposoby wyborufrob
wywoł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*/ } }
źródło