2014-06-25 15 views
11

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

  1. Der Typ von &B::g ist int (A::*)(), anstatt int (B::*)();
  2. Obwohl eine implizite Konvertierung von int (A::*)() zu int (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 auf nullptr_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?

+0

Es ist wahrscheinlich einfacher, all das in Nichtmitgliedsfunktionen zu verpacken und diesen Albtraum des Foo (Bar :: *) (Baz) ein für allemal zu vergessen. –

+0

@ 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

+0

@DDrmmr 'F' ist eine Vereinfachung von' boost :: multi_index :: mem_fun'. Ich möchte, dass sie kompilieren. – subi

Antwort

4

Ich habe einen Workaround gefunden, der meinen Anforderungen entspricht. Ich hoffe, es ist hilfreich für jeden in Not. Ich bin seit Tagen festgefahren ...

Die Idee ist, eine Funktionsvorlage zu verwenden, um eine bestimmte Überladung zu entsprechen, und dann den richtigen Typ an G übergeben. Eine indirekte Ebene rettet immer die Welt.

template <typename T> 
auto forward_pmf_with_signature(int(T::*pmf)()) -> decltype(pmf); 

G<decltype(forward_pmf_with_signature(&B::f)), &B::f> {}; // works 

G wird, ohne das Bewusstsein der A verwendet und wählt die richtige Überlast, cool.

Jetzt ist das neue Problem, dass ich gefunden G<decltype(forward_pmf_with_signature(&B::f)), &B::f> ist redundant und fehleranfällig. Es ist trivial, einen Makro USE_G(&B::f) einfach den Code zu verwenden, aber es scheint mir, dass es nicht einfach ist, oder sogar möglich, die Vereinfachung in einer semantischen Weise zu tun.

+0

Gute Arbeit, aber Sie brauchen keinen Körper für 'forward_pmf_with_signature()'. Sie können es stattdessen als Deklaration lassen, da alle Compiler-Anforderungen der Rückgabetyp sind. – 0x499602D2

+0

@ 0x499602D2 danke, bearbeitet. – subi