Подключение сигналов к слотам с меньшим количеством параметров разрешено в Qt?

Это действительно, чтобы позвонить

QObject::connect(a, SIGNAL(somesig(someparam)), b, SLOT(someslot()));

без параметров? Кажется, это работает (без исключений во время выполнения), но я не могу найти ссылку в документах. Все, что я обнаружил, это то, что это возможно, если у someslot есть параметр по умолчанию. Это действительно в этом случае. Но мой метод someslot имеет не тот же набор параметров, что и по умолчанию (здесь нет параметров в примере).

Таким образом, кажется возможным подключить сигналы к слотам с меньшими параметрами?

2 ответа

Решение

Да, это нормально. В документации Signals & Slots об этом есть короткое предложение:

[...] Подпись сигнала должна соответствовать подписи принимающего слота. (На самом деле слот может иметь более короткую сигнатуру, чем сигнал, который он получает, потому что он может игнорировать дополнительные аргументы.) [...]

Внизу страницы есть даже такой пример, где объясняются аргументы по умолчанию.

С точки зрения стандарта C++ решение Qt также работает.

Передача сигнала осуществляется путем вызова метода:

emit someSignal(3.14);

emit Ключевое слово на самом деле разрешается до пустого #defineтак что строка выше просто вызывает метод someSignal с заданными аргументами. Метод, возможно, был объявлен внутри QObjectкласс получился так:

class SomeObject: public QObject {
    Q_OBJECT
public slots:
    void firstSlot() { /* implementation */ }
    void secondSlot(double) {  /* implementation */ }

signals:
    void someSignal(double);  /* no implementation here */
};

Это должно показаться вам знакомым, но вы, возможно, задавались вопросом, откуда происходит фактическая реализация ваших сигналов. Как вы можете догадаться, именно здесь начинается мета-компилятор (MOC) Qt. Для каждого метода, объявленного в signals раздел, который он предоставляет в своем сгенерированном источнике реализацию, которая выглядит примерно так:

void SomeObject::someSignal(double _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
}

Интересная часть void *_a[] вектор, который заполнен указателями на аргументы, передаваемые сигналу. Ничего особенного здесь нет.

Вектор аргумента передается QMetaObject::activate, который, в свою очередь, выполняет некоторые проверки безопасности потока и другую служебную работу, а затем начинает вызывать слоты, которые были подключены к сигналу, если таковые имеются. Поскольку соединения сигнал-слот разрешаются во время выполнения (способ, которым connect() работает), небольшая помощь от MOC требуется снова. В частности, МОС также генерирует qt_static_metacall() Реализация вашего класса:

void SomeObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        SomeObject *_t = static_cast<SomeObject *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->firstSlot(); break;
        case 1: _t->secondSlot((*reinterpret_cast< double(*)>(_a[1]))); break;
        default: ;
        }
    } /* some more magic */
}

Как видите, этот метод содержит другой конец для разрешения void *_a[] вектор до вызова функции. Также вы можете видеть, что нет списка переменных аргументов (используя многоточие, ...) или другой сомнительный обман.

Итак, чтобы прояснить исходный вопрос: когда, например, someSignal(double) подключен к secondSlot(double) что соответствует сигнатуре сигнала, вызов разрешается в case 1 в qt_static_metacall и он просто передает аргумент, как и ожидалось.

При подключении сигнала к firstSlot() который имеет меньше аргументов, чем сигнал, вызов разрешается case 0 а также firstSlot() вызывается без аргументов. Аргумент, который был передан сигналу, просто остается нетронутым в его void *_a[] вектор.

Другие вопросы по тегам