Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
544 views
in Technique[技术] by (71.8m points)

c++ - Connecting overloaded signals and slots in Qt 5

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?

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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){ /* ... */ });

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...