There are several ways to connect signal and slots. The first is to use function pointers: connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed); There are several advantages to using QObject::connect() with function pointers.
It is generally unsafe to provide slots in your QThread subclass, unless you protect the member variables with a mutex. On the other hand, you can safely emit signals from your QThread::run() implementation, because signal emission is thread-safe.
[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) Creates a connection of the given type from the signal in the sender object to the method in the receiver object.
The problem here is that there are two signals with that name: QSpinBox::valueChanged(int)
and QSpinBox::valueChanged(QString)
. From Qt 5.7, there are helper functions provided to select the desired overload, so you can write
connect(spinbox, qOverload<int>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
For Qt 5.6 and earlier, you need to tell Qt which one you want to pick, by casting it to the right type:
connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
I know, it's ugly. But there's no way around this. Today's lesson is: do not overload your signals and slots!
Addendum: what's really annoying about the cast is that
void
(for signals).So I've found myself sometimes using this C++11 snippet:
template<typename... Args> struct SELECT {
template<typename C, typename R>
static constexpr auto OVERLOAD_OF( R (C::*pmf)(Args...) ) -> decltype(pmf) {
return pmf;
}
};
Usage:
connect(spinbox, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged), ...)
I personally find it not really useful. I expect this problem to go away by itself when Creator (or your IDE) will automatically insert the right cast when autocompleting the operation of taking the PMF. But in the meanwhile...
Note: the PMF-based connect syntax does not require C++11!
Addendum 2: in Qt 5.7 helper functions were added to mitigate this, modelled after my workaround above. The main helper is qOverload
(you've also got qConstOverload
and qNonConstOverload
).
Usage example (from the docs):
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)
Addendum 3: if you look at the documentation of any overloaded signal, now the solution to the overloading problem is clearly stated in the docs themselves. For instance, https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1 says
Note: Signal valueChanged is overloaded in this class. To connect to this signal by using the function pointer syntax, Qt provides a convenient helper for obtaining the function pointer as shown in this example:
connect(spinBox, QOverload<const QString &>::of(&QSpinBox::valueChanged), [=](const QString &text){ /* ... */ });
The error message is:
error: no matching function for call to
QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))
The important part of this is the mention of "unresolved overloaded function type". The compiler doesn't know whether you mean QSpinBox::valueChanged(int)
or QSpinBox::valueChanged(QString)
.
There are a handful of ways to resolve the overload:
connect()
QObject::connect<void(QSpinBox::*)(int)>(spinBox, &QSpinBox::valueChanged,
slider, &QSlider::setValue);
This forces connect()
to resolve &QSpinBox::valueChanged
into the overload that takes an int
.
If you have unresolved overloads for the slot argument, then you'll need to supply the second template argument to connect()
. Unfortunately, there's no syntax to ask for the first to be inferred, so you'll need to supply both. That's when the second approach can help:
void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged;
QObject::connect(spinBox, signal,
slider, &QSlider::setValue);
The assignment to signal
will select the desired overload, and now it can be substituted successfully into the template. This works equally well with the 'slot' argument, and I find it less cumbersome in that case.
We can avoid static_cast
here, as it's simply a coercion rather than removal of the language's protections. I use something like:
// Also useful for making the second and
// third arguments of ?: operator agree.
template<typename T, typename U> T&& coerce(U&& u) { return u; }
This allows us to write
QObject::connect(spinBox, coerce<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
Actually, you can just wrap your slot with lambda and this:
connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
will be look better. :\
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With