[英]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種不同的類型, int
, float
, char
, double
,一些enum
和struct
。 產生的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
。另外,請隨時在代碼本身上留下注釋。
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.