簡體   English   中英

QObject模板類型推導的多重繼承的復雜性

[英]Complexities of multiple inheritance of QObject for template type deduction

背景

我相信我已經提出了解決Qt中臭名昭著的模板信號和插槽問題的解決方案。 首先,我定義了一個空的Message基類,其唯一目的是繼承並創建具體的TMessage實現。 在Qt中,雖然可以將信號連接到函子(而不是Qt插槽),但信號不能是模板函數,因此我的解決方案是對Message類的關系進行建模,從而創建抽象Messenger類,帶有一個void signal_Message(Message)信號和一個template <typename T> void slot_T(TMessage<T>) 然后,我遇到了這個問題和這個問題,並意識到這是一個更易於維護的解決方案,並且我實質上創建了一個Messenger包。 當使用不受支持的類型時,偶數代碼在編譯時失敗,並顯示一條不錯的可讀錯誤消息: invalid initialization of reference of type Messenger<double>& from expression of type MessengerBag<int, char>

問題

考慮我們使用6種不同的類型, intfloatchardouble ,一些enumstruct 產生的sizeof 96個字節! 但是,鑒於期望的用法是用於50種以上用戶定義類型的場所,每種類型都有各自單獨維護的信號和插槽容器,因此看來我的解決方案可以在大約相同的開銷下執行相同的任務,但是沒有任何維護費用。 除了不再需要維護24個獨立功能的明顯好處(在復雜的繼承方案中可能更多)之外,這種方法是否還有缺點? 經過一些初步測試,我發現MessengerBag本質上與菱形圖案相反。 每個析構函數都被相應地調用,因此與復合結構相比,我幾乎看不到任何缺點,因為復合結構需要另一級別的(我認為是不必要的和模糊的)間接。 Qt本質上是鄙視虛擬繼承,並禁止在直接繼承QObject的類中使用它:因此,我無法像我希望的那樣使Mock虛擬。

// Message.h
struct Message {};
template <typename T> struct TMessage {
    T t
};

// Messenger.h
struct Messenger : public QObject {
    Q_OBJECT
public:
    template <typename T> void slot_doSomethingWithMsg(const Message& msg){
        const auto& tmsg = static_cast<const TMessage<T>&>(msg);
        qDebug() << tmsg.t;
        // do something else... this is just example usage
    }
signals:
    void signal_sendMsg(const BaseMsg& msg);

// MessengerBag.h

template <typename T, typename... Args>
struct MessengerBag : TMessenger<T>, MessengerBag<Args...>{};

template <typename T> struct MessengerBag<T> : TMessenger<T>{};

// Manager.h

MessengerBag<int, char> messengerBag;

Manager(){
    QObject::connect(static_cast<TMock<int>*>(messengerBag),
                     &Mock::signal_sendMsg, 
                     static_cast<TMock<int>*>(messengerBag),
                     &Mock::slot_doSomethingWithMsg<int>);

    QObject::connect(static_cast<TMock<char>*>(messengerBag),
                     &Mock::signal_sendMsg, 
                     static_cast<TMock<char>*>(messengerBag),
                     &Mock::slot_doSomethingWithMsg<char>);

template <typename T> void slot_sendMsg(const TMessage<T>& msg){
    TMessenger<T>& messenger = messengerBag;
    messenger.signal_sendMsg(msg);
}

// main.cpp

auto* manager = new Manager();

TMessage<int> imsg{5};
TMessage<char> cmsg{'f'};

manager->slot_sendMsg(imsg);
manager->slot_sendMsg(cmsg);

不出所料,上面的示例打印了5和'f',因此用模板信號和插槽來解決問題。 但是,我在將來遇到任何可能的問題時會遇到一些麻煩。 一個問題當然是QObject不可訪問,因為哪個QObject仍然是未知的,哪個合成解決方案可能會進行調解(使TMessenger委托給Messenger ,而MessengerBag以空的Args專業化繼承QObject 。另外,請隨時在代碼本身上留下注釋。

TL;博士

Qt信號調用函子,但信號不能是模板。 為了使用基Message類創建模板樣式信號,讓對象繼承從QObject派生的多個模板類有什么弊端? Message是信號的參數(因此信號和模板函子具有基本版本(此后函子將其強制轉換為正確的類型T)。結構將像這些模板類的包一樣,繼承所有模板類為了允許在任何時候上傳到正確的模板類。

只是在這個鏡頭...

template<typename _Tb>
class _Ts : public _Tb 
{

public:
_Ts() {

// your connects

}

// any extensions to the qobject classes you are using
// note that you will need a common interface in your qobject subclasses... 
// meaning same named slots with different implementations 

};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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