模板类型推导的QObject多重继承的复杂性

时间:2018-12-19 18:43:45

标签: c++ qt c++11 multiple-inheritance

背景

我相信我已经提出了解决Qt中臭名昭著的模板信号和插槽问题的解决方案。首先,我定义了一个空的基础Message类,其唯一目的是继承并创建具体的TMessage实现。在Qt中,虽然可以将信号连接到函子(而不是Qt插槽),但是信号不能是模板函数,因此我的解决方案也是对Message类的关系进行建模,创建一个带有Messenger信号和void signal_Message(Message)的抽象template <typename T> void slot_T(TMessage<T>)类。然后,我遇到了this question和这个问题,并意识到这是一个更易于维护的解决方案,并且我实质上创建了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,而{{1} }以空置的MessengerBag专长继承QObject。在不首先了解其可能存在的缺点的情况下,我犹豫不决地致力于此解决方案。此外,请随时对代码本身发表评论。

tl; dr

Qt信号称为函子,但信号不能是模板。为了使用基类Args创建模板样式信号,让对象继承从QObject派生的多个模板类有什么弊端? Message是信号的参数(因此信号和模板函子具有基本版本(此后函子将其强制转换为正确的类型T)。结构就像这些模板类的包一样,继承所有它们,以便可以随时向上投射到正确的模板类。

1 个答案:

答案 0 :(得分:0)

只是为此开枪...

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 

};