2016-03-25 14 views
8

Załóżmy, że wszystkie klasy hierarchii implementują funkcję elementu szablonu g. Wszystkie klasy podziela tą realizację dwóch innych funkcji f1 i f2 które wymagają tego szablonu:Wywołanie funkcji polimorficznej bez duplikatu kodu

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

Od implementacji f1 i f2 są identyczne we wszystkich klasach, w jaki sposób mogę pozbyć duplikatu kodu i nadal czy wywołanie polimorficzne w main działa zgodnie z oczekiwaniami (tzn. generuje wyjście "In B")?

Odpowiedz

3

Zauważ, że implementacje f1 i f2 w A, B i Cnie identyczne. Ograniczmy to do f1 s. Jedna wywołuje funkcję o nazwie ::A::g<int>, inna wywołuje funkcję o nazwie ::B::g<int>, a trzecia wywołuje funkcję o nazwie ::C::g<int>. Są one bardzo dalekie od z identycznego.

Najlepiej można zrobić, to mieć CRTP -Style Base:

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

patrzę na to w inny sposób: wszyscy nazywają 'this-> g ', gdzie '* this' jest dynamiczny typ' A'. Dlaczego to niewłaściwy sposób patrzenia? – AlwaysLearning

+1

@AlwaysLearning Ponieważ 'g' nie jest (i nie może być) wirtualny, co oznacza, że ​​mają do niego zastosowanie normalne zasady dotyczące kompilacji. Zwróć uwagę, że reguły kompilacji są domyślne w C++. Istnieje tylko kilka wybranych wyjątków od tego ('virtual',' dynamic_cast', 'typeid'). – Angew

+0

Dobrze. Ale 'f1' jest wirtualny. Wewnątrz 'f1' typ' this' jest dynamicznym typem 'a'. Wszystko, czego chcę, to mieć tę samą, identyczną implementację 'f1' w klasie pochodnej bez potrzeby powtarzania implementacji ... – AlwaysLearning

1

Można się tylko czynnikiem rzeczy klasy specyficzne, że funkcja szablon zastosowań, takich jak (w swoim przykładzie) nazwę klasy :

#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

To był tylko przykład. W rzeczywistości implementacje 'g' różnią się bardzo. – AlwaysLearning

+0

@AlwaysLearning: Nie mogę odpowiedzieć na pytanie, które nie zostało postawione. Przepraszam. Nie telepatycznie. –

+0

Masz całkowitą rację. – AlwaysLearning