背景
我相信我已经想出了一个解决方案来解决Qt中臭名昭著的模板信号和插槽问题。对于初学者,我定义了一个空的基类Message
,它的唯一目的是继承并创建具体的TMessage
实现。在Qt中,虽然可以将信号连接到函数器(而不是Qt槽),但信号不能是模板函数,所以我的解决方案是对Message
类的关系进行建模,创建一个抽象的Messenger
类,使用void signal_Message(Message)
信号和template <typename T> void slot_T(TMessage<T>)
。然后我遇到了this question和这个问题,并意识到这是一个更易维护的解决方案,我基本上创建了一堆Messenger
。当使用不支持的类型时,even代码在编译时失败,并显示一条漂亮的、可读的错误消息:invalid initialization of reference of type Messenger<double>& from expression of type MessengerBag<int, char>
问题
考虑我们使用6种不同的类型,int
、float
、char
、double
、一些enum
和一个struct
。这将产生一个96字节的sizeof
!然而,假设期望的用法是针对有超过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;dr
Qt信号调用函数器,但信号不能是模板。为了通过使用基类Message
来创建模板样式的信号,让一个对象继承从QObject
派生的多个模板类有什么缺点吗?此Message
是信号的参数(以便信号和模板函数器具有基本版本(之后函数器将其转换为正确的类型T)。结构就像是这些模板类的袋子,继承所有这些模板类,以便随时向上转换到正确的模板类。
https://stackoverflow.com/questions/53857413
复制相似问题