2016-03-25 7 views
8

Angenommen, alle Klassen einer Hierarchie implementieren eine Vorlagenelementfunktion g. Alle Klassen teilen sich die gleiche Implementierung von zwei weiteren Funktionen f1 und f2 die diese Vorlage nennen:Polymorpher Funktionsaufruf ohne doppelten Code

struct A { 
    virtual void f1() { 
     g(5); 
    } 
    virtual void f2() { 
     g(5.5); 
    } 
private: 
    template <typename T> void g(T) {std::cout << "In A" << std::endl;} 
}; 

struct B: A { 
    // Can I get rid of this duplicate code? 
    virtual void f1() { 
     g(5); 
    } 
    virtual void f2() { 
     g(5.5); 
    } 
private: 
    template <typename T> void g(T) {std::cout << "In B" << std::endl;} 
}; 

struct C: A { 
    // Can I get rid of this duplicate code? 
    virtual void f1() { 
     g(5); 
    } 
    virtual void f2() { 
     g(5.5); 
    } 
private: 
    template <typename T> void g(T) {std::cout << "In C" << std::endl;} 
}; 

int main() 
{ 
    B b; 
    A &a = b; 
    a.f1(); 
    return 0; 
} 

Da die Implementierungen von f1 und f2 sind identisch in allen Klassen, wie kann ich den doppelten Code loszuwerden und noch hat der polymorphe Aufruf in main funktioniert wie erwartet (dh produzieren die Ausgabe "In B")?

Antwort

3

Beachten Sie, dass die Implementierungen von f1 und f2 in A, B und Cnicht identisch sind. Beschränken wir es auf f1 s. Man ruft eine Funktion namens ::A::g<int>, eine andere ruft eine Funktion namens ::B::g<int>, und die dritte ruft eine Funktion namens ::C::g<int>. Sie sind sehr weit von identisch.

Das Beste, was Sie tun können, ist eine CRTP -Stil Basis haben:

template <class Derived> 
struct DelegateToG : public A 
{ 
    void f1() override 
    { 
    static_cast<Derived*>(this)->g(5); 
    } 

    void f2() override 
    { 
    static_cast<Derived*>(this)->g(5.5); 
    } 
}; 

class B : public DelegateToG<B> 
{ 
    friend DelegateToG<B>; 
private: 
    template <class T> void g(T) { /*...*/ } 
}; 

class C : public DelegateToG<C> 
{ 
    friend DelegateToG<C>; 
private: 
    template <class T> void g(T) { /*...*/ } 
}; 
+0

ich einen anderen Weg, um es aussehen: alle nennen sie 'this-> g ', wo '* this' der dynamische Typ von' a' ist. Warum ist das der falsche Weg? – AlwaysLearning

+1

@AlwaysLearning Da 'g' nicht virtuell ist (und nicht sein kann), bedeutet dies, dass normale Regeln für die Kompilierung gelten. Beachten Sie, dass Kompilierzeitregeln die Standardeinstellung in C++ sind. Es gibt nur einige wenige explizite Ausnahmen ('virtual',' dynamic_cast', 'typeid'). – Angew

+0

Rechts. Aber 'f1' ist virtuell. Innerhalb von 'f1' ist der Typ von' this' der dynamische Typ von 'a'. Alles, was ich will, ist die gleiche identische Implementierung von 'f1' in der abgeleiteten Klasse, ohne die Implementierung wiederholen zu müssen ... – AlwaysLearning

1

Sie können nur die klassenspezifische Dinge berücksichtigen, dass die Template-Funktion verwendet, wie (in Ihrem Beispiel) den Klassennamen :

#include <iostream> 
using namespace std; 

class A 
{ 
private: 
    virtual auto classname() const -> char const* { return "A"; } 

protected: 
    template <typename T> void g(T) {cout << "In " << classname() << endl;} 

public: 
    virtual void f1() { g(5); } 
    virtual void f2() { g(5.5); } 
}; 

class B 
    : public A 
{ 
private: 
    auto classname() const -> char const* override { return "B"; } 
}; 

class C 
    : public A 
{ 
private: 
    auto classname() const -> char const* override { return "C"; } 
}; 

auto main() 
    -> int 
{ static_cast<A&&>(B()).f1(); } 
+0

Dies war nur ein Beispiel. In der Tat variieren die Implementierungen von "g" sehr. – AlwaysLearning

+0

@AlwaysLearning: Ich kann eine Frage, die Sie nicht gestellt haben, nicht beantworten. Es tut uns leid. Nicht telepathisch. –

+0

Sie sind völlig richtig. – AlwaysLearning

Verwandte Themen