145

I'm having trouble getting to grips with the new signal/slot syntax (using pointer to member function) in Qt 5, as described in New Signal Slot Syntax. I tried changing this:

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

to this:

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

but I get an error when I try to compile it:

error: no matching function for call to QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

I've tried with clang and gcc on Linux, both with -std=c++11.

What am I doing wrong, and how can I fix it?

Toby Speight
  • 23,550
  • 47
  • 57
  • 84
dtruby
  • 1,585
  • 2
  • 11
  • 9
  • If your syntax is right, then the only explanation could be that you aren't linking to the Qt5 libraries, but e.g. Qt4 instead. This is easy to verify with QtCreator on the 'Projects' page. – Matt Phillips May 28 '13 at 14:37
  • I included some subclasses of QObject (QSpinBox etc.) so that should have included QObject. I did try adding that include as well though and it still won't compile. – dtruby May 28 '13 at 14:40
  • Also, I'm definitely linking against Qt 5, I'm using Qt Creator and the two kits I'm testing with both have Qt 5.0.1 listed as their Qt version. – dtruby May 28 '13 at 14:41

4 Answers4

264

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

  1. one repeats the class name twice
  2. one has to specify the return value even if it's usually 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){ /* ... */ });
peppe
  • 19,728
  • 3
  • 47
  • 65
  • 1
    Ah yeah, that makes a lot of sense. I guess for cases like this where the signals/slots are overloaded I'll just stick with the old syntax :-). Thanks! – dtruby May 28 '13 at 15:22
  • 17
    I was so excited about the new syntax ... now a cold splash of freezing disappointment. – RushPL Oct 17 '13 at 14:10
  • In VS2013 the SELECT snipped is giving me `error C2144: syntax error : 'auto' should be preceded by ';'` – Tim MB Sep 01 '14 at 17:44
  • Ah, removing `constexpr` fixed it. Not sure if that breaks it in other compilers – Tim MB Sep 01 '14 at 17:50
  • Thank you so much, had this for QProcess::finished, which I didn't think was overloaded, but gave this error: cannot convert parameter 2 from 'overloaded-function' to 'const char *' – gollumullog Dec 15 '14 at 20:03
  • Check this out ('error' getter and 'error' signal) http://doc.qt.io/qt-5/qprocess.html#error-2Even QT guys do this, so this is totally legit... workaround is sure ugly :( – xchg.ca May 12 '15 at 19:13
  • 12
    For those wondering (like me): "pmf" stands for "pointer to member function". – Vicky Chijwani Jul 02 '15 at 10:41
  • 16
    I personally prefer the `static_cast` ugliness over the old syntax, simply because [the new syntax enables a compile-time check](http://wiki.qt.io/New_Signal_Slot_Syntax) for the existence of the signal/slot where the old syntax would fail at runtime. – Vicky Chijwani Jul 02 '15 at 10:43
  • The same error also happens when signal name is not ambiguous but it takes a parameter: for example QDialog::finished. Why is the cast needed in this case? – MirkoBanchi Sep 13 '15 at 11:40
  • 2
    Unfortunately, not overloading a signal is often not an option—Qt often overloads their own signals. (e.g. `QSerialPort`) – PythonNut Mar 01 '16 at 15:47
  • I guess it should be `QOverload` and not `qOverload`? Edit: rip me, `QOverload` works on c++11 only, `qOverload` requires c++14. – Cubimon Jul 30 '19 at 11:32
16

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:

  • Provide a suitable template parameter to 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:

  • Use a temporary variable of the correct type

    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.

  • Use a conversion

    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);
    
Toby Speight
  • 23,550
  • 47
  • 57
  • 84
8

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. :\

Newlifer
  • 137
  • 1
  • 4
  • 8
0

The solutions above work, but I solved this in a slightly different way, using a macro, So just in case here it is:

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

Add this in your code.

Then, your example:

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

Becomes:

QObject::connect(spinBox, CONNECTCAST(QSpinBox, double, valueChanged),
             slider, &QSlider::setValue);
Basile Perrenoud
  • 3,810
  • 1
  • 24
  • 47
  • 2
    Solutions "above" what? Don't assume that answers are presented to everyone in the order you're currently seeing them! – Toby Speight Sep 19 '17 at 16:09
  • 1
    How do you use this for overloads that take more than one argument? Doesn't the comma cause problems? I think you really need to pass the parens, i.e. `#define CONNECTCAST(class,fun,args) static_cast(&class::fun)` - used as `CONNECTCAST(QSpinBox, valueChanged, (double))` in this case. – Toby Speight Sep 19 '17 at 16:12
  • it's a nice useful macro when brackets are used for multiple arguments, as in Toby's comment – ejectamenta Apr 06 '18 at 14:57