Подключение перегруженных сигналов и слотов в Qt 5



у меня возникли проблемы с освоением нового синтаксиса сигнала/слота (с использованием указателя на функцию-член) в Qt 5, как описано в Новый Синтаксис Слота Сигнала. Я попытался изменить это:



QObject::connect(spinBox, SIGNAL(valueChanged(int)),
slider, SLOT(setValue(int));


для этого:



QObject::connect(spinBox, &QSpinBox::valueChanged,
slider, &QSlider::setValue);


но я получаю ошибку, когда я пытаюсь скомпилировать его:




ошибка: нет подходящей функции для вызова QObject::connect(QSpinBox*&,
<unresolved overloaded function type>, QSlider*&, void
(QAbstractSlider::*)(int))




Я пробовал с clang и gcc на Linux, как с -std=c++11.



что я делаю неправильно и как я могу это исправить?

1068   4  

4 ответов:

проблема здесь в том, что есть два сигналы с таким именем: QSpinBox::valueChanged(int) и QSpinBox::valueChanged(QString). В Qt 5.7 предусмотрены вспомогательные функции для выбора нужной перегрузки, поэтому вы можете написать

connect(spinbox, qOverload<int>(&QSpinBox::valueChanged),
        slider, &QSlider::setValue);

для Qt 5.6 и более ранних версий вам нужно сказать Qt, какой из них вы хотите выбрать, приведя его к правильному типу:

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
        slider, &QSlider::setValue);

Я знаю, это некрасиво. Но нет никакого пути вокруг этого. Сегодняшний урок: не перегружайте сигналы и слоты!


дополнительное соглашение: что действительно раздражает в актерском составе, так это

  1. один повторяет имя класса два раза
  2. нужно указать возвращаемое значение, даже если это обычно void (сигналов).

поэтому я обнаружил, что иногда использую этот фрагмент C++11:

template<typename... Args> struct SELECT { 
    template<typename C, typename R> 
    static constexpr auto OVERLOAD_OF( R (C::*pmf)(Args...) ) -> decltype(pmf) { 
        return pmf;
    } 
};

использование:

connect(spinbox, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged), ...)

Я лично считаю, что это не очень полезно. Я ожидал проблема уйти сама по себе, когда Создатель (или ваша IDE) автоматически вставит правильный бросок при автозавершении операции взятия PMF. Но пока что ... ..

Примечание: синтаксис подключения на основе PMF не требует C++11!


приложение 2: в Qt 5.7 были добавлены вспомогательные функции для смягчения этого, смоделированные после моего обходного пути выше. Главным помощником является qOverload (у вас также qConstOverload и qNonConstOverload).

пример использования (из документов):

struct Foo {
    void overloadedFunction();
    void overloadedFunction(int, QString);
};

// requires C++14
qOverload<>(&Foo:overloadedFunction)
qOverload<int, QString>(&Foo:overloadedFunction)

// same, with C++11
QOverload<>::of(&Foo:overloadedFunction)
QOverload<int, QString>::of(&Foo:overloadedFunction)

сообщение об ошибке:

ошибка: нет подходящей функции для вызова QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

важной частью этого является упоминание о "неразрешенный перегруженный тип функции". Компилятор не знает, имеете ли вы в виду QSpinBox::valueChanged(int) или QSpinBox::valueChanged(QString).

существует несколько способов устранения перегрузки:

  • обеспечивать соответствующий параметр шаблона connect()

    QObject::connect<void(QSpinBox::*)(int)>(spinBox, &QSpinBox::valueChanged,
                                             slider,  &QSlider::setValue);
    

    это сил connect() разрешить &QSpinBox::valueChanged в перегрузку, которая принимает int.

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

  • используйте временную переменную правильного типа

    void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged;
    QObject::connect(spinBox, signal,
                     slider,  &QSlider::setValue);
    

    задание signal выделит нужную перегрузку, и теперь ее можно успешно подставить в шаблон. Это одинаково хорошо работает с аргументом "слот", и я нахожу его менее громоздким в этом случае.

  • используйте преобразование

    мы можем избежать static_cast здесь, как это просто принуждение, а не удаление защиты языка. Я использую что-то вроде:

    // Also useful for making the second and
    // third arguments of ?: operator agree.
    template<typename T, typename U> T&& coerce(U&& u) { return u; }
    

    это позволяет нам писать

    QObject::connect(spinBox, coerce<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
                     slider, &QSlider::setValue);
    

на самом деле, вы можете просто обернуть свой слот с лямбды и этого:

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
    slider, &QSlider::setValue);

будет выглядеть лучше. :\

решения выше работают, но я решил это немного по-другому, используя макрос, так что на всякий случай вот он:

#define CONNECTCAST(OBJECT,TYPE,FUNC) static_cast<void(OBJECT::*)(TYPE)>(&OBJECT::FUNC)

добавьте это в свой код.

потом, ваш пример:

QObject::connect(spinBox, &QSpinBox::valueChanged,
             slider, &QSlider::setValue);

будет:

QObject::connect(spinBox, CONNECTCAST(QSpinBox, double, valueChanged),
             slider, &QSlider::setValue);

Comments

    Ничего не найдено.