qtlambdasignals-slots

What is the functional difference between these two qt signal-slot connections?


I'm using Qt 5.9.2 with Visual Studio 2015 and QtDesigner for programming a Windows GUI application. I tried connecting one of my actions via the following call:

connect(ui.myAction, &QAction::triggered, memberPtrToObjX_, &ClassX::Run);

However ClassX::Run is not always triggered after clicking on myAction in the menubar. Investigating into this problem, I figured, that the same signal-slot connection using lambda syntax works:

connect(ui.myAction, &QAction::triggered, [this](bool run) { memberPtrToObjX_->Run(run); });

I'm pretty sure, that both calls are syntactically correct. Besides both calls return a valid QMetaObject::Connection, if I save the return value and check with operator bool().

Obviously I could just stick with the working lambda-version, but I'm confused and would prefer knowing the reason behind my "solution". Is there any functional difference between these two calls, that explains the different behaviour?


Solution

  • The two calls of QObject::connect() (exposed by the OP) behave differently in the case that this->memberPtrToObjX_ is modified after the call of connect().

    The first

    connect(ui.myAction, &QAction::triggered, memberPtrToObjX_, &ClassX::Run);
    

    calls
    QMetaObject::Connection QObject::connect(
    const QObject *sender, PointerToMemberFunction signal,
    const QObject *receiver, PointerToMemberFunction method,
    Qt::ConnectionType type = Qt::AutoConnection)
    .

    Creates a connection of the given type from the signal in the sender object to the method in the receiver object. Returns a handle to the connection that can be used to disconnect it later.

    Hence, the current pointer in this->memberPtrToObjX_ is connected as signal receiver. If this->memberPtrToObjX_ is modified after connect() this doesn't have any effect to the signal connection.

    The second

    connect(ui.myAction, &QAction::triggered, [this](bool run) { memberPtrToObjX_->Run(run); });
    

    calls
    QMetaObject::Connection QObject::connect(
    const QObject *sender, PointerToMemberFunction signal,
    Functor functor)
    .

    Creates a connection from signal in sender object to functor, and returns a handle to the connection.

    Hence, the (functor behind the) lambda is connected as receiver. The lambda resolves the pointer in this->memberPtrToObjX_ at the time it is executed i.e. when the signal is triggered.


    The second difference (which originally was uncovered in the comment of G.M.) is the connection type:

    The first version uses the default value Qt::AutoConnection (as it is not defined explicitly). The version with the lambda uses always Qt::DirectConnection instead.

    The difference appears if the pointee in this->memberPtrToObjX_ does not "live" in the same thread. In this case, the Qt::AutoConnection is resolved to Qt::QueuedConnection instead of Qt::DirectConnection.

    I assumed that the pointee in this->memberPtrToObjX_ would "live" in the same QThread. If not, the second version (with the lambda) becomes very questionable as it calls the a member function of the object "living" in a different thread (where it is hard to tell what that thread is doing at this time). This only seems to work better but is very possibly a "time bomb".