为什么 Qt 信号不是 const

Why are Qt signals NOT const

Qt 使用 signals and slots 进行对象通信。 信号通常声明为成员函数,然后 Qt MOC 生成该函数的定义。

我想了解的是为什么信号不是常量成员函数?

编辑: 我希望信号不会修改发件人,这就是问题所在。

I would expect signals not to modify the sender

信号(如generated by the MOC)不直接修改class实例的成员。然而,生成的代码会传递一个 this 指针,供(潜在的)插槽使用。因此,连接的插槽可能会改变信号的发送者。

所以技术原因是,如果信号是 const,则需要所有插槽实现只调用发送方的 const class 成员来编译代码没有错误。

将信号实现为非const class 成员是一个可以理解的决定,考虑到代码安全。在许多情况下仍然感觉不自然(例如,如果在同一个 class 中实现的连接槽是 const,或者如果连接槽完全属于另一个对象)。

没有什么能阻止 Qt 信号成为 const AFAIK(使用 Qt5.9 测试)。 IInspectable 的答案不正确。

下面是我做的一个测试,表明它仍然可以
- 将 const 信号 连接到同一实例中的 non-const 槽
- 在 sender().

上调用 non-const 方法

我测试的 class,编译良好 (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;
};

这里是 MOC 为信号生成的代码:

// 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);
}

我们可以看到 Qt 在 this 上使用 const_cast 所以一切都会正常工作。


在我看来,默认情况下信号不是 const 的原因是这需要 MOC 添加一个 const 到你的 信号(== class-method ) 在你的 header 中定义,因此修改你的源代码。

我想这可以通过将信号的每个定义包含在宏中来实现,但想象一下编码器和 reader 的痛苦。我认为您将信号声明为 const 对 Qt(您也一样)没有任何好处,这需要您和 Qt 做更多的工作。

但是您有时可能需要将它们声明为 const。就像你想从 const 方法中发出它们一样。你可以自由地这样做。

信号可以是常量,事实上如果你想从一个常量函数发出一个信号,它必须是一个常量信号,但在插槽端它不需要是常量。在使用 const pures 重新实现抽象 类 时,我已经摆脱了必须以这种方式使用可变变量的麻烦。