Hintergrund
den folgenden Code vor:Die Nicht-Typ Template-Parameter der Zeiger auf überladene Elementfunktionen mit Vererbung
struct A { // a class we want to hide from the client code
int f();
int f(char);
int g();
};
struct B : A {}; // the interface class
// client code:
//
// It's natural to assume a member function of T should have
// a type of int (T::*)(), right?
template <typename T, int (T::*)()> struct F {};
// compiles, of course
F<A, &A::g> {};
// doesn't compile, because &B::g is not of type `int (B::*)()`, and no
// conversion is allowed here
F<B, &B::g> {};
// compiles, because &B::g is actually &A::g
//
// but this is not I want because the class hierarchy may be deep and it's
// not easy to know what A is.
F<A, &B::g> {};
Das Fragment struct<B, &B::g> {}
lässt sich nicht kompilieren, weil
- Der Typ von
&B::g
istint (A::*)()
, anstattint (B::*)()
; - Obwohl eine implizite Konvertierung von
int (A::*)()
zuint (B::*)()
legal und praktikabel ist, verbietet der C++ - Standard (!!) jegliche Konvertierung, wenn die Template-Argument-Substitution für den Point-to-Member-Funktion-Template-Parameter durchgeführt wird. (Streng genommen eine Umstellung aufnullptr_t
erlaubt ist.)
Als Folge F<B, &B::g>
kann die genaue Definition von F
nicht überein, und es nicht zu kompilieren. Das ist traurig, weil class A
das Implementierungsdetail sein kann, mit dem wir uns nicht befassen wollen.
Umgehung
Ein naiver Hack würde
template <typename T, T val> struct G {};
// at least compiles, and don't have to be aware of A
G<decltype(&B::g), &B::g> {};
so weit, so gut.
Problem
Die obige Hack mit überladenen Klassenmethoden nicht funktioniert. Normalerweise können die Überladungen durch static_cast aufgelöst werden, aber dies erfordert, dass wir den genauen Typ von & B :: f kennen - eine nette Küken-und-Ei-Situation.
// won't compile since &B::f is ambiguous
G<decltype(&B::f), &B::f> {};
// won't compile just like F<B, &B::g>
G<decltype(static_cast<int(B::*)()>(&B::f)), &B::f> {};
// compiles, but require the knowledge of the type of &B::f
G<decltype(static_cast<int(A::*)()>(&B::f)), &B::f> {};
Etwas wie G<decltype(static_cast<int(A::*)()>(&B::f)), &B::f> {};
ist schrecklich.
Um zu schließen, ist das Problem, wie man eine bestimmte Überlastung korrekt auswählt und vermeiden, die Basisklasse A
zu erwähnen, wenn &B::f
tatsächlich &A::f
ist.
Irgendwelche Gedanken?
Es ist wahrscheinlich einfacher, all das in Nichtmitgliedsfunktionen zu verpacken und diesen Albtraum des Foo (Bar :: *) (Baz) ein für allemal zu vergessen. –
@ n.m. Ja, es ist Albtraum und ich kann sie in viele Proxy-Funktionen einbinden, um es einfach funktionieren zu lassen, aber ich frage mich, ob es auf eine bequeme Weise behoben werden kann. – subi
@DDrmmr 'F' ist eine Vereinfachung von' boost :: multi_index :: mem_fun'. Ich möchte, dass sie kompilieren. – subi