Подключение перегруженных сигналов и слотов в 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.
что я делаю неправильно и как я могу это исправить?
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);Я знаю, это некрасиво. Но нет никакого пути вокруг этого. Сегодняшний урок: не перегружайте сигналы и слоты!
дополнительное соглашение: что действительно раздражает в актерском составе, так это
- один повторяет имя класса два раза
- нужно указать возвращаемое значение, даже если это обычно
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