2011-09-30 58 views
4

我有一個類層次結構,我想介紹一個方法模板,它的行爲就像虛擬一樣。例如一個簡單的層次結構:如何模擬方法模板的虛擬性

class A { 
    virtual ~A() {} 

    template<typename T> 
    void method(T &t) {} 
}; 

class B : public A { 
    template<typename T> 
    void method(T &t) {} 
}; 

然後,我創建對象B:

A *a = new B(); 

我知道我可以通過typeid(a)存儲在a類型。當我知道該類型時,如何動態調用正確的B::method?我可能有這樣的情況:

if(typeid(*a)==typeid(B)) 
    static_cast<B*>(a)->method(params); 

但我想,以避免類似的情況。我正在考慮創建一個std::map作爲關鍵字typeid,但是我會把它作爲一個價值?

回答

1

如您所知,由於虛擬函數的整體屬於類類型的一部分並且必須事先知道,因此不能爲虛擬函數提供模板。這排除了任何簡單的「任意重寫」。

如果它是一個選項,你可以使類的模板參數部分:

template <typename T> class A 
{ 
protected: 
    virtual void method(T &); 
}; 

template <typename T> class B : public A<T> 
{ 
    virtual void method(T &); // overrides 
}; 

一個更復雜的方法可能使用一些調度對象:

struct BaseDispatcher 
{ 
    virtual ~BaseDispatcher() { } 
    template <typename T> void call(T & t) { dynamic_cast<void*>(this)->method(t); } 
}; 
struct ConcreteDispatcher : BaseDispatcher 
{ 
    template <typename T> void method(T &); 
}; 

class A 
{ 
public: 
    explicit A(BaseDispatcher * p = 0) : p_disp(p == 0 ? new BaseDispatcher : p) { } 
    virtual ~A() { delete p_disp; }; 
private: 
    BaseDispatcher * p_disp; 
    template <typename T> void method(T & t) { p_disp->call(t); } 
}; 

class B : public A 
{ 
public: 
    B() : A(new ConcreteDispatcher) { } 
    // ... 
}; 
+0

我不能那樣做。我真的需要一個方法模板。 –

+0

@JurajBlaho:我又增加了一個想法。 –

+0

@JurajBlaho:恩,[使成員模板虛擬只是不能在C + +](http://stackoverflow.com/questions/2354210/can-a-member-function-template-be-virtual/2354671#2354671) ,所以你將不得不重新考慮你的方法,並對語言的限制做出一些讓步。 – sbi

2

是否有任何共同的代碼,你可以提取和虛擬?

class A { 
    virtual ~A() {} 

    template<typename T> 
    void method(T &t) 
    { 
     ... 
     DoSomeWork(); 
     ... 
    } 

    virtual void DoSomeWork() {} 
}; 

class B : public A { 
    virtual void DoSomeWork() {} 
}; 
+0

純粹的常識驅動!這有幫助,謝謝! – Arun

6

您可以使用「奇異遞歸模板模式」 http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

地使用這種模式,基類在派生類類型作爲模板參數,這意味着基類可以施展自己的派生類型以便調用派生類中的函數。這是一種編譯時虛擬函數的實現,不需要做虛擬函數調用。

template<typename DERIVED_TYPE> 
class A { 
public: 
    virtual ~A() {} 

    template<typename T> 
    void method(T &t) { static_cast<DERIVED_TYPE &>(*this).methodImpl<T>(t); } 
}; 

class B : public A<B> 
{ 
friend class A<B>; 

public: 
    virtual ~B() {} 

private: 
    template<typename T> 
    void methodImpl(T &t) {} 
}; 

然後,它可以像這樣使用...

int one = 1; 
A<B> *a = new B(); 
a->method(one); 
+1

在這種情況下不會有基類,所以你不能存儲這些類。在一個向量中,因爲A類是一個模板。但是,您可以從一個常見的ABase繼承,但在這種情況下,您無法從Base中訪問該方法。 –

+0

@工業抗抑鬱藥是的,這種方法有明顯的缺點,例如不能在容器中存儲不同的派生類型,但這仍然是一個有用的設計模式。 – Alan

0

糟糕。最初回答at the wrong question - 不錯啊,在另一個問題

經過一番思考我認識到了這個經典的多方法要求,即一個方法,它能將基於多個參數的運行時類型 。通常的虛擬功能是single dispatch比較(和他們派遣的類型this只)。

請參考以下:

  • 安德烈Alexandrescu的寫上實施使用泛型在 '現代C++設計'
    • Chapter 11: "Multimethods"多方法(嗎?C++精液比特) - 它實現了基本的多方法,使它們成爲對數(使用有序的類型列表),然後一直到恆定時間的多方法。相當強大的東西!
  • 一個codeproject article,似乎剛纔這樣的實現:
    • 沒有用型投下任何種類的(動態,靜態,重新詮釋,const或C型)
    • 沒有用的RTTI ;
    • 不使用預處理器;
    • 強型安全;
    • 單獨編譯;
    • 多方法執行的恆定時間;
    • 在multimethod調用期間沒有動態內存分配(通過new或malloc);
    • 不使用非標準庫;
    • 只使用標準的C++功能。
  • C++ Open Method Compiler,彼得Pirkelbauer,尤里Solodkyy和Bjarne的Stroustrup的
  • 洛基圖書館有A MultipleDispatcher
  • 維基百科有與C++在多分派的例子相當不錯simple write-up

這裏是從維基百科文章供參考 '簡單' 的方法(在以下簡單的方法擴展爲較大數量的派生類型的更好):

// Example using run time type comparison via dynamic_cast 

struct Thing { 
    virtual void collideWith(Thing& other) = 0; 
} 

struct Asteroid : Thing { 
    void collideWith(Thing& other) { 
     // dynamic_cast to a pointer type returns NULL if the cast fails 
     // (dynamic_cast to a reference type would throw an exception on failure) 
     if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) { 
      // handle Asteroid-Asteroid collision 
     } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) { 
      // handle Asteroid-Spaceship collision 
     } else { 
      // default collision handling here 
     } 
    } 
} 

struct Spaceship : Thing { 
    void collideWith(Thing& other) { 
     if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) { 
      // handle Spaceship-Asteroid collision 
     } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) { 
      // handle Spaceship-Spaceship collision 
     } else { 
      // default collision handling here 
     } 
    } 
}