2016-03-25 29 views
8

Si supponga che tutte le classi di una gerarchia implementino una funzione membro modello g. Tutte le classi condividono la stessa realizzazione di altre due funzioni f1 e f2 che chiamano questo modello:Chiamata di funzione polimorfica senza codice duplicato

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; 
} 

Dal momento che le implementazioni di f1 e f2 sono identiche in tutte le classi, come posso liberarmi del codice duplicato ed ancora la chiamata polimorfica in main funziona come previsto (ovvero produce l'uscita "In B")?

risposta

3

noti che le implementazioni di f1 e f2 in A, B e C sono non identici. Limitiamolo a f1 s. Si chiama una funzione denominata ::A::g<int>, un'altra chiama una funzione denominata ::B::g<int> e la terza chiama una funzione denominata ::C::g<int>. Sono molto lontani da identici.

La migliore che si possa fare è avere una base di CRTP in stile:

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

lo guardo un altro modo: tutti li chiamano 'this-> g ', dove '* this' è il tipo dinamico di' a'. Perché questo è il modo sbagliato di guardare? – AlwaysLearning

+1

@AlwaysLearning Perché 'g' non è (e non può essere) virtuale, il che significa che a esso si applicano normali regole di compilazione. Nota che le regole di compilazione sono predefinite in C++. Esistono solo poche eccezioni esplicite a questo ('virtuale',' dynamic_cast', 'typeid'). – Angew

+0

Giusto. Ma 'f1' è virtuale. All'interno di 'f1' il tipo di' this' è il tipo dinamico di 'a'. Tutto quello che voglio è avere la stessa identica implementazione di 'f1' nella classe derivata senza dover ripetere l'implementazione ... – AlwaysLearning

1

Si può solo fattore le cose specifiche della classe che la funzione di modello utilizza, ad esempio (nel tuo esempio) il nome della classe :

#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

Questo era solo un esempio. In effetti, le implementazioni di 'g' variano molto. – AlwaysLearning

+0

@AlwaysLearning: non posso rispondere a una domanda che non hai posto. Scusate. Non telepatico. –

+0

Hai perfettamente ragione. – AlwaysLearning

Problemi correlati