Załóżmy uprościć po prostu biorąc pod uwagę różnicę między:
template <class C, class M> void f(C (M::*member)());
template <class C, class M> void g(C (M::*member));
W f
, member
jest wskaźnikiem do funkcji członkowskim M
powracającego przy zerowych argumenty i powrocie C
. Jeśli wywołasz go pod numerem &B::func
, kompilator wyprowadzi M == B
i C == void
. Bezpośredni.
W g
, member
jest tylko wskaźnikiem do elementu M
typu C
. Ale w naszym przypadku &B::func
jest funkcją. Więc wpływ tutaj polega na porzuceniu wskaźnika. Ponownie dedukujemy M == B
, natomiast C
staje się void()
- teraz C
jest typem funkcji. Jest to mniej wyspecjalizowana wersja niż f
, ponieważ pozwala na więcej rodzajów członków. g
można dopasować do funkcji, które pobierają argumenty lub do wskaźników do członków, lub, odpowiednio, do cv -wyprofilowanych funkcjach członkowskich.
Rozważmy jako przykład przeciążony funkcja i jak to się wywnioskować inaczej (to był oryginalny problem w swoim pytaniu, który został edytowany, ale nadal jest interesujący):
struct X {
void bar() { }
void bar(int) { }
};
kiedy zrobić:
f(&X::bar);
Nawet &X::bar
jest przeciążony nazwa, tylko jeden faktycznie pasuje C (M::*)()
. Ten, który ma M == X
i C == void
. Po prostu nie ma mowy, aby przeciążenie z bar
przyniosło int
dopasowanie do typu szablonu. Jest to jedno z dopuszczalnych zastosowań przekazywania przeciążonej nazwy. To prowadzi do grzywny.
Jednak gdy robimy:
g(&X::bar);
Teraz istnieje dwa doskonale vaild potrącenia. C
może być zarówno void()
i void(int)
. Ponieważ oba są prawidłowe, dedukcja jest niejednoznaczna i nie można skompilować - z błędem, który tak naprawdę nie czyni tego szczególnie wyraźnie.
Wracając do przykładu:
call1(&B::func2, this); // Error: no matching member function for call to 'call2'
call2(&B::func2, this); // works
Rodzaj &B::func2
jest void (B::*)() const volatile
. Od call1
wywodzi się na typ funkcji członka, który nie przyjmuje argumentów i nie jest kwalifikowany cv, odliczenie typu po prostu nie powiedzie się.Nie ma żadnego C
lub M
, aby dopasować te typy.
Odliczenie call2
jest w porządku, ponieważ jest bardziej ogólne. Może on pasować do każdego wskaźnika o wartości dowolnej. Po prostu kończymy z C = void() const volatile
. Właśnie dlatego to działa.
Nie wiedziałem nawet, że 'C (M :: * member)' jest prawidłową składnią ... –
Chodzi o to, że 'call1' nie przyjmuje funkcji z kwalifikatorami cv, a' call2' pozwala na to. – Crossfire
Niepowiązane, ale po co zawracać sobie głowę użyciem 'std :: bind' zamiast bezpośrednio wywoływać funkcję member? '(instance -> * member)();' –