簡體   English   中英

給定抽象基礎 class X,如何創建另一個模板 class D<t> 其中 T 是從 X 派生的 class 的類型?</t>

[英]given abstract base class X, how to create another template class D<T> where T is the type of the class deriving from X?

我希望能夠接受引用Message1Message2 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 類型的messageMessage ,而其動態類型可能是Message1Message2 模板對動態類型一無所知,也無法對其進行操作。 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> ,具體取決於messageMessage1還是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_typeintDerived2::value_typedouble - 允許每個算法高效並使用適當的舍入等。同樣對於不同的容器類型,只使用共享的 API。
  • 您可以使用模板元編程、SFINAE 等以特定於派生類型的方式自定義行為

就個人而言,我認為應用這種技術的知識和能力(但很少)是掌握多態性的重要組成部分。

如前所述,不可能按原樣構建您的模板。

我認為傳遞附加參數沒有任何問題,盡管我可能會將它們打包成一個結構,以便於操作。

當然,我發現使用補充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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM