Mac
Mac

Reputation: 3559

Why are Qt signals NOT const

Qt uses signals and slots for object communication. Signals are normally declared as a member function and the Qt MOC then generates the definition of that function.

What I would like to understand is why signals are not const member functions?

Edit: I would expect signals not to modify the sender, that's why the question.

Upvotes: 24

Views: 7808

Answers (3)

Mike
Mike

Reputation: 302

Signals can be const, in fact if you want to emit a signal from a const function it must be a const signal, but on the slot end it doesn't need to be const. I've gotten out of having to use mutables this way when reimplementing abstract classes with const pures.

Upvotes: 3

ymoreau
ymoreau

Reputation: 3996

Nothing prevents a Qt signal to be const AFAIK (tested with Qt5.9). The answer from IInspectable is not correct.

Below is a test I made to show it is still possible to
- connect a const signal to a non-const slot in the same instance
- call non-const methods on the sender().

The class of my test, which compiles fine (gcc) :

// Stupid class to test the emit of a const-signal
class Test : public QObject
{
    Q_OBJECT

public:
    // Connect in the constructor from this instance to this instance
    explicit Test(QObject *parent = nullptr) : QObject(parent) {
        connect(this, &Test::valueChanged, this, &Test::updateString);
    }

    // To test a non-const method call
    void nonConstFoo() {
        setObjectName("foo"); // I modify the 'this' instance
    }

    // To test emit of a non-const signal from a const-method
//    void constFoo() const {
//        emit fooSignal(); // --> FAIL at compile time
//    }

public slots:
    void setValue(int value) {
        m_value = value;
        emit valueChanged(value);
    }

    void updateString(int value) {
        m_string = QString::number(value); // I modify the 'this' instance
        nonConstFoo(); // I modify the 'this' instance through a non-const call

        auto s = sender();
        s->setObjectName("mutated name"); // I modify the 'sender' instance

        qDebug() << "Updated string" << m_string;
    }

signals:
    void valueChanged(int) const; // The signal is const
    void fooSignal(); // Non-const signal

private:
    int m_value;
    QString m_string;
};

And here is the code generated by the MOC for the signals :

// SIGNAL 0
void Test::valueChanged(int _t1)const
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(const_cast< Test *>(this), &staticMetaObject, 0, _a);
}

// SIGNAL 1
void Test::fooSignal()
{
    QMetaObject::activate(this, &staticMetaObject, 1, nullptr);
}

We can see that Qt uses const_cast on this so everything will work anyway.


In my opinion, the reason the signals are not const by default is that this would require the MOC to add a const to your signal (== class-method) definition in your header, therefore modify your source code.

I guess this would be doable by enclosing each definition of signal in a macro, but imagine the pain for the coder and for the reader. I do not see any gain for Qt (nor you) that you have your signals declared as const, and this would require more work for you and Qt.

But you may need to declare them as const sometimes. Like when you want to emit them from a const method. And you are free to do so.

Upvotes: 16

IInspectable
IInspectable

Reputation: 51413

I would expect signals not to modify the sender

Signals (as generated by the MOC) do not directly modify a class instance' members. The generated code, however, passes a this pointer along, for consumption by a (potential) slot. A connected slot could thus mutate the sender of the signal.

So the technical reason is, that if signals were const, it would require that all slot implementations would only call const class members on the sender for the code to compile without errors.

Implementing signals as non-const class members is an understandable decision, with respect to code safety. It still feels unnatural in a number of cases (e.g. if the connected slot implemented in the same class is const, or if the connected slot belongs to another object altogether).

Upvotes: 19

Related Questions