在 Qt 5 中连接过载的信号和插槽

2021-12-09 00:00:00 qt5 qt c++

我无法掌握 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));

为此:

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

但是当我尝试编译它时出现错误:

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

错误:没有匹配的函数调用QObject::connect(QSpinBox*&,<未解析的重载函数类型>, QSlider*&, void(QAbstractSlider::*)(int))

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

我在 Linux 上尝试过使用 clang 和 gcc,都使用 -std=c++11.

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?

推荐答案

这里的问题是有两个具有该名称的信号:QSpinBox::valueChanged(int)QSpinBox::valueChanged(QString)代码>.从 Qt 5.7 开始,提供了帮助函数来选择所需的重载,因此您可以编写

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);

对于 Qt 5.6 及更早版本,您需要通过将其转换为正确的类型来告诉 Qt 您想要选择哪一个:

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. 一个重复两次类名
  2. 即使返回值通常是void(对于信号),也必须指定返回值.
  1. one repeats the class name twice
  2. one has to specify the return value even if it's usually void (for signals).

所以我发现自己有时会使用这个 C++11 代码片段:

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;
    } 
};

用法:

connect(spinbox, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged), ...)

我个人觉得它不是很有用.我希望当 Creator(或您的 IDE)在自动完成获取 PMF 的操作时自动插入正确的演员表时,这个问题会自行消失.但与此同时...

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

注意:基于 PMF 的连接语法不需要 C++11!

Note: the PMF-based connect syntax does not require C++11!

附录 2:在 Qt 5.7 中添加了辅助函数来缓解这种情况,以我上面的解决方法为模型.主要的帮手是 qOverload(你我也有 qConstOverloadqNonConstOverload).

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)

<小时>

附录 3:如果您查看任何重载信号的文档,现在文档本身清楚地说明了重载问题的解决方案.例如,https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1 说


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

注意:信号 valueChanged 在此类中被重载.为了使用函数指针语法连接到这个信号,Qt 提供了一个方便的助手来获取函数指针,如下例所示:

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

相关文章