QT:使用移动语义将信号连接到插槽

Posted

技术标签:

【中文标题】QT:使用移动语义将信号连接到插槽【英文标题】:QT: Connect signals to slots with move semantics 【发布时间】:2017-10-02 08:10:23 【问题描述】:

好的,我有这样的事情:

void EmailReceiverThread::foreachEmailAccount(ConfigEmailAccount account)

    Email email;
    EmailReader emailReader(account);
    while (emailReader.pollEmail(email))
    
        writeEmailToDatabase(email);
    

ForeachEmailAccount 是 EmailReceiverThread 中的一个槽。它是从 run() 方法调用的:

GetConfigEmailDAO* accountReader = new GetConfigEmailDAO(type, m_channel);
connect(accountReader, SIGNAL(emailAccount(ConfigEmailAccount)),
        this, SLOT(foreachEmailAccount(ConfigEmailAccount)), Qt::QueuedConnection);
DbThread::postAction(accountReader);

GetConfigEmailDAO 进行查询,获取电子邮件帐户并发出:

emit emailAccount(account);

到目前为止,这有效。问题是,我正在复制类 ConfigEmailAccount。我想避免那种性能冲击。所以,我的想法是使用 c++11 移动语义发出帐户:

emit emailAccount(std::move(account));

我用新语法重写了槽和信号:

void foreachEmailAccount(ConfigEmailAccount&& account);

不幸的是,这不起作用。我有下一个编译错误:

moc_emailreceiverthread.cpp:88: error: cannot bind ‘ConfigEmailAccount’ lvalue to ‘ConfigEmailAccount&&’
     case 1: _t->foreachEmailAccount((*reinterpret_cast< ConfigEmailAccount(*)>(_a[1]))); break;

我能做什么?

此处是可验证的最小示例:

#include <QCoreApplication>
#include <QObject>

class ConfigEmailAccount

    QString some_data;
;

class GetConfigEmailDAO : public QObject

    Q_OBJECT
public:
    void execute();

signals:
    void emailAccount(ConfigEmailAccount&& account);
;

void GetConfigEmailDAO::execute()

    ConfigEmailAccount account;
    emit emailAccount(std::move(account));


class EmailReceiverThread : public QObject

    Q_OBJECT
public:
    void execute();
public slots:
    void foreachEmailAccount(ConfigEmailAccount&& account);
;

void EmailReceiverThread::execute()


    GetConfigEmailDAO* accountReader = new GetConfigEmailDAO;
    connect(accountReader, SIGNAL(emailAccount(ConfigEmailAccount)),
            this, SLOT(foreachEmailAccount(ConfigEmailAccount)), Qt::QueuedConnection);
    accountReader->execute();


void EmailReceiverThread::foreachEmailAccount(ConfigEmailAccount&& account)




int main(int argc, char *argv[])

    QCoreApplication a(argc, argv);
    qRegisterMetaType<ConfigEmailAccount>("ConfigEmailAccount");
    EmailReceiverThread reader;
    reader.execute();
    return a.exec();


#include "main.moc"

【问题讨论】:

我敢打赌,Qt 的信号和插槽中没有对移动语义和右值引用的特殊支持,因为它们是在 C++11 之前的版本中设计的。因此,您最好使用 Qt 的本机解决方案来避免大量复制:或者重新设计 ConfigEmailAccount 类以使用 implicit sharing,或者,如果很难或不可能,则使用 QSharedPointer&lt;ConfigEmailAccount&gt; 作为信号和插槽。 你可以简单地通过指针传递你的对象。 为什么不把它包装成std::shared_ptr&lt;ConfigEmailAccount&gt; 并传递呢? 如果将多个插槽连接到同一个信号会发生什么? 【参考方案1】:

最后我采用了QT的浅拷贝机制:http://doc.qt.io/qt-5.6/qshareddatapointer.html#details

【讨论】:

以上是关于QT:使用移动语义将信号连接到插槽的主要内容,如果未能解决你的问题,请参考以下文章

Qt将信号连接到插槽

如何将信号连接到插槽

Qt4 现有插槽无法识别

Qt for Qt:如何连接到具有新信号/插槽风格的信号?

Qt Connect 无法连接到插槽

将 QML 信号连接到 PySide2 插槽