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