2016-07-28 20 views
-1

Ich verstehe, warum Sie nicht einfach einen abgeleiteten Klassenmitgliedsfunktionszeiger auf Basisfunktionszeiger des Basismitglieds wie erläutert here umwandeln können.Übergeben abgeleitete virtuelle Überschreibung, um reines virtuelles Mitglied zu basieren

Aber angesichts dieser Ausschnitt:

struct base 
{ 
    virtual void foo() = 0; 
}; 

struct derived : base 
{ 
    void foo() override {}; 
}; 

struct invoker 
{ 
    typedef void(base::*target)(); 

    invoker(base* b, target t) 
    { 
     (b->*t)(); 
    } 
}; 

template<typename B, typename D> 
void (B::*cast(void (D::*method)()))() 
{ 
    return static_cast<void(B::*)()>(method); 
} 

derived d; 
invoker bad(&d, &derived::foo); //C2664 
invoker good(&d, cast<base>(&derived::foo)); 

ich fragen wollte, ist es möglich, die Basisfunktion Unterschrift zu dekorieren, so dass Compiler es versteht, ist eine rein virtuelle Methode und, und es wird irgendwo in der Hierarchie implementiert werden (sonst könnte ich kein Objekt vom Typ B konstruieren)? Ich verstehe, warum ich das mit normalen Funktionen nicht tun kann, aber IMHO im Falle einer reinen virtuellen Funktion hat der Compiler eine Garantie, dass es implementiert wird (falls es nicht gemacht wurde, würde ich einen Fehler über die Klasse B nicht über die Besetzung).

+1

Es ist unklar, um welche Art von Dekoration es sich handelt. Wenn Sie ein oberster Leiter des C++ - Design-Komitees wären, was würden Sie tun? Machen Sie sich keine Gedanken über Konsistenz oder Korrektheit, zeigen Sie einfach, was Sie zu C++ hinzufügen möchten. Wenn Sie dem Compiler nur mitteilen wollen, dass 'derived :: foo 'in' base' existiert, können Sie einfach '& base :: foo' schreiben. –

+0

@ n.m. Ich möchte es vermeiden, die explizite Besetzung zu machen: D Aber es war eher eine Art "Frage, ob ich mich nicht bewusst bin". –

+1

Nein, nicht einen Wurf machen. Beginnen Sie einfach mit '& base :: foo'. Was macht '& abgeleitete :: foo', was' & base :: foo' nicht kann? –

Antwort

2

Es ist nicht notwendig, den Typ &derived::foo zu manipulieren. Man kann stattdessen einfach &base::foo verwenden.

Zeiger auf Mitgliedsfunktionen respektieren die Virtualität. Dieser Aufruf

base* pBase = new derived; 
auto pFoo = &base::foo; 
(pBase->*pFoo)(); 

wird derived::foo eigentlich nennen, genau wie ein einfacher Anruf pBase->foo() würde.

0

Auch wenn es eine Garantie für die Implementierung gibt, werden möglicherweise zusätzliche Datenelemente verwendet, die nur in derived deklariert sind. Eine mögliche Lösung besteht darin, Funktionszeiger zu verwenden und this als ersten Parameter zu übergeben (dies zeigt Ihnen auch, warum Sie dies nicht über virtual tun können).

+0

Da ich 'base *' übergebe, würde ich aufgrund von Laufzeitpolymorphismus in der korrekten Überschreibung enden, so dass ich auf nichts zugreifen kann, das nicht da ist. Oder habe ich dich falsch verstanden? –

+0

Fügen Sie ein 'int i;' hinzu und tun Sie 'i = 0xDEADBEEF;' zu 'derived :: foo()', um zu sehen, was ich meine. Es funktioniert umgekehrt: Sie können den "Base" -Funktionszeiger übergeben, wenn ein "abgeleiteter" erwartet wird. Beachten Sie, dass * Sie * wissen, dass Sie 'abgeleitet *' übergeben, aber der C++ - Compiler könnte es nicht wissen. – lorro

+0

Nein, ich kann nicht, da ich keine Instanz von 'Base' konstruieren kann, da es ein reines virtuelles Element hat . Ich spreche nicht über den allgemeinen Fall, sondern über einen, bei dem das Ziel der Besetzung ein rein virtuelles Mitglied der Basis ist. –

0

Betrachten Sie die folgende Diamant-Hierarchie:

struct base { 
virtual void foo() = 0; 
}; 

struct D1 : public virtual base { 
virtual void foo() override; 
}; 

struct D2 : public virtual base { 
virtual void foo() override; 
}; 

struct Derived : public virtual D1, D2 { 
virtual void foo() final; 
}; 

Jetzt ein Szenario, in dem ein upcast von Abgeleitet ist erlaubt :: * zu stützen :: *. Welche Funktion sollte aufgerufen werden? Der Compiler verliert Informationen darüber, welche von D1 :: foo, D2 :: foo oder Derived :: foo Sie aufrufen möchten, da diese Information weggeworfen wurde. Um diese Art von Mehrdeutigkeit zu vermeiden, ist ein solcher Upcast nicht zulässig.

+0

Ich denke, mein Punkt des Scheiterns war, dass die Vtable-Lookup auch auf Funktionszeiger passiert (und in diesem Fall würde die richtige Methode tatsächlich aufgerufen werden), aber ich denke, dass es nicht passiert. –

+0

"_Welche Funktion sollte aufgerufen werden? _" 'Foo()', denke ich – curiousguy