[英]given abstract base class X, how to create another template class D<T> where T is the type of the class deriving from X?
我希望能夠接受引用Message1
或Message2
class 的Message&
object。 我希望能夠根據Message&
object 的基礎類型創建MessageWithData<Message1>
或MessageWithData<Message2>
。 例如,見下文:
class Message {};
class Message1 : public Message {};
class Message2 : public Message {};
template<typename Message1or2>
class MessageWithData : public Message1or2 { public: int x, y; }
class Handler()
{
public:
void process(const Message& message, int x, int y)
{
// create object messageWithData whose type is
// either a MessageWithData<Message1> or a MessageWithData<Message2>
// based on message's type.. how do I do this?
//
messageWithData.dispatch(...)
}
};
messageWithData class 本質上包含從 Message 繼承的方法,這些方法允許根據其類型動態地將其雙重分派回處理程序。 到目前為止,我最好的解決方案是將數據與消息類型分開,並一直通過動態調度鏈傳遞它,但我希望更接近動態雙重調度的真正習慣,其中消息類型包含可變數據。
(我或多或少遵循的方法來自http://jogear.net/dynamic-double-dispatch-and-templates )
您正在嘗試混合運行時和編譯時概念,即(運行時)多態性和模板。 對不起,但那是不可能的。
模板在編譯時對類型進行操作,也稱為static types 。 static 類型的message
是Message
,而其動態類型可能是Message1
或Message2
。 模板對動態類型一無所知,也無法對其進行操作。 Go 具有運行時多態性或編譯時多態性,有時也稱為 static 多態性。
運行時多態方法是訪問者模式,具有雙重調度。 下面是一個使用CRTP 習慣用法的編譯時多態性示例:
template<class TDerived>
class Message{};
class Message1 : public Message<Message1>{};
class Message2 : public Message<Message2>{};
template<class TMessage>
class MessageWithData : public TMessage { public: int x, y; };
class Handler{
public:
template<class TMessage>
void process(Message<TMessage> const& m, int x, int y){
MessageWithData<TMessage> mwd;
mwd.x = 42;
mwd.y = 1337;
}
};
你有
void process(const Message& message, int x, int y)
{
// HERE
messageWithData.dispatch(...)
}
在這里,您想要創建MessageWithData<Message1>
或MessageWithData<Message2>
,具體取決於message
是Message1
還是Message1
的實例。
但是您不能這樣做,因為 class 模板 MessageWithData MessageWithData<T>
需要在編譯時知道T
應該是什么,但是在運行時通過調度到message
之前,該類型在代碼中不可用。
正如 Xeo 所說,在這種特殊情況下您可能不應該這樣做 - 存在更好的設計替代方案。 也就是說,您可以使用 RTTI 來做到這一點,但它通常不受歡迎,因為您的process()
成為一個集中維護點,需要在添加新的派生類時進行更新。 這很容易被忽視並且容易出現運行時錯誤。
如果您出於某種原因必須堅持這一點,那么至少要概括該設施,以便單個 function 使用基於 RTTI 的運行時類型確定來調用任意行為,如下所示:
#include <iostream>
#include <stdexcept>
struct Base
{
virtual ~Base() { }
template <class Op>
void for_rt_type(Op& op);
};
struct Derived1 : Base
{
void f() { std::cout << "Derived1::f()\n"; }
};
struct Derived2 : Base
{
void f() { std::cout << "Derived2::f()\n"; }
};
template <class Op>
void Base::for_rt_type(Op& op)
{
if (Derived1* p = dynamic_cast<Derived1*>(this))
op(p);
else if (Derived2* p = dynamic_cast<Derived2*>(this))
op(p);
else
throw std::runtime_error("unmatched dynamic type");
}
struct Op
{
template <typename T>
void operator()(T* p)
{
p->f();
}
};
int main()
{
Derived1 d1;
Derived2 d2;
Base* p1 = &d1;
Base* p2 = &d2;
Op op;
p1->for_rt_type(op);
p2->for_rt_type(op);
}
在上面的代碼中,您可以替換您自己的 Op 並進行相同的運行時到編譯時切換。 將其視為反向工廠方法可能有幫助,也可能無濟於事:-}。
如前所述,必須為每個派生類型更新for_rt_type
:如果一個團隊“擁有”基礎 class 而其他團隊編寫派生類,則尤其痛苦。 與許多稍微有點 hacky 的東西一樣,它在支持私有實現方面更實用和可維護,而不是作為低級企業庫的 API 功能。 想要使用它通常仍然是其他地方糟糕設計的標志,但並非總是如此:偶爾會有算法( Op
)受益匪淺:
Derived1::value_type
是int
, Derived2::value_type
是double
- 允許每個算法高效並使用適當的舍入等。同樣對於不同的容器類型,只使用共享的 API。就個人而言,我認為應用這種技術的知識和能力(但很少)是掌握多態性的重要組成部分。
如前所述,不可能按原樣構建您的模板。
我認為傳遞附加參數沒有任何問題,盡管我可能會將它們打包成一個結構,以便於操作。
當然,我發現使用補充Data
參數更慣用,而不是擴展 class 層次結構將所有這些硬塞到一個模式中。
試圖使設計適合模式是一種反模式。 正確的方法是調整模式,使其適合設計。
話雖如此...
您的解決方案有多種選擇。 Inheritance 看起來很奇怪,但如果沒有整個設計,它可能是你最好的選擇。
已經提到過,您不能隨意混合編譯時和運行時多態性。 我通常使用 Shims 來規避這個問題:
class Message {};
template <typename T> class MessageShim<T>: public Message {};
class Message1: public MessageShim<Message1> {};
該方案很簡單,可讓您從兩全其美中受益:
Message
非模板意味着您可以應用傳統的OO策略MessageShim<T>
是模板意味着您可以應用傳統的通用編程策略一旦完成,你應該能夠得到你想要的,無論好壞。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.