2013-10-01 18 views
5

Próbuję zbudować ogólny algorytm. Do tej pory udało mi się osiągnąć to za pomocą klasy hierarchii i wskazówek, jak w poniższym przykładzie:C++ raczej statyczny niż dynamiczny polimorfizm

struct Base{ 
    virtual double fn(double x){return 0;} 
}; 

class Derived : public Base{ 
    double A; 
public: 
    Derived(double a) : A(a) {} 
    double fn(double x) { return A*x;} 
}; 

//Some other implementations 

class algo{ 
    double T; 
    std::unique_ptr<Base> b_ptr; 
public: 
    algo(double t, std::unique_ptr<Base>& _ptr); //move constructor... 
    //Some constructors 
    double method(double x){ return T*b_ptr->fn(x);} 

}; 

Ten zestaw jest następnie realizowany w następujący sposób:

int main(){ 
    std::unique_ptr<Derived> ptr(new Derived(5.4)); 
    algo(3.2,ptr); 
    method(2.4); 

    return 0; 
} 

Jest to bardzo prosty przykład, oczywiście , ale służy do mojego pytania. Z tego co rozumiem, używanie klas pochodnych w ten sposób oznacza, że ​​metoda jest wybierana w czasie wykonywania, a nie w czasie kompilacji. Ponieważ nie potrzebuję żadnego dynamicznego zachowania z mojego algorytmu - wszystko jest ustalane w czasie kompilacji - jest to niepotrzebna utrata wydajności. Czy istnieje sposób na wykonanie powyższych czynności w czasie kompilacji, czyli statyczny polimorfizm?

Z tego co rozumiem, można uzyskać statyczny polimorfizm za pomocą szablonów. Nie udało mi się jednak znaleźć szablonów narzędzi z typami nieprostymi. Jak w powyższym przykładzie, potrzebuję klas pochodnych z nie-domyślnymi konstruktorami, co nie wydaje się możliwe ... Czy ktokolwiek mógłby zaproponować rozwiązania, jak to zrobić?

+0

swoje obserwacje na temat niepotrzebny dynamiczne zachowanie jest przenikliwe: Powinieneś mieć tylko polimorficzną hierarchię klas, jeśli * musisz * odroczyć decyzję, jaki typ rzeczywisty potrzebujesz do wykonania. Dobrymi przykładami są parsowanie komunikatów z protokołu sieciowego lub obsługa zdarzeń w pętli zdarzeń. W przeciwnym razie, jeśli faktycznie * wiesz * jakiego typu potrzebujesz, polimorfizm funkcji wirtualnych nie jest właściwym narzędziem. –

+0

'możliwe jest uzyskanie statycznego polimorfizmu za pomocą szablonów' Polimorfizm polega na wywołaniu prawidłowej podstawy funkcji na bieżącym typie obiektu. Chciałbym więc powiedzieć, że przeciążeniem funkcji jest również statyczny polimorfizm. – ZijingWu

Odpowiedz

3

klasy bazowej i pochodne wydają się reprezentować funkcję tylko o jedną funkcję Członka, więc mogliśmy najprawdopodobniej rezygnacji z polimorfizmem całkowicie i przekazać funkcję w algo:

#include <iostream> 
#include <utility> 

template <class Function> 
class algo 
{ 
    double t; 
    Function fn; 

public: 
    algo(double t, const Function& fn) 
     : t{t}, fn{fn} 
    { } 
    double method(double x){ return t * fn(x);} 

}; 

template <class Function> 
algo<Function> make_algo(double t, Function&& fn) 
{ 
    return algo<Function>(t, std::forward<Function>(fn)); 
} 

int main() 
{ 
    const double someValue = 123; 
    const double anotherValue = 987; 

    auto algo = make_algo(anotherValue, [someValue](double otherValue) { 
     return someValue * otherValue; 
    }); 

    std::cout << std::fixed << algo.method(321) << std::endl; 
} 
+0

Możesz uogólnić to dalej i sprawić, że podwójny będzie również parametrem szablonu. – goji

+0

To bardzo ciekawa odpowiedź, wielkie dzięki! Przykład klasy jednofunkcyjnej był prosty. Zwykle klasa pochodna ma wiele funkcji, a funkcje mogą być dość złożone. Metoda przekazywania funkcji byłaby zbyt nieczytelna! Czy można wcześniej zdefiniować funkcje, a następnie przekazać je? Właśnie odkryłem opakowanie std :: function, czy jest ono podobne do powyższej metody czy używa dynamicznego polimorfizmu? – Plamen

+0

Użycie 'std :: function' zamiast podania funkcji jako szablonu prawdopodobnie spowodowałoby spowolnienie kodu. Inną alternatywą jest używanie klas polityk, może to być bardziej ścieżka, na którą liczyłeś ze statycznym wiązaniem twoich algorytmów. – goji