繁体   English   中英

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

[英]Connecting overloaded signals and slots in 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))

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

我做错了什么,我该如何解决?

这里的问题是有两个具有该名称的信号: 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);

我知道,这很丑陋 但是没有办法解决这个问题。 今天的教训是:不要让你的信号和插槽过载!


附录:演员阵容真正令人讨厌的是

  1. 一个重复两次类名
  2. 必须指定返回值,即使它通常是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 (你也有qConstOverloadqNonConstOverload )。

使用示例(来自文档):

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

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

 connect(spinBox, QOverload<const QString &>::of(&QSpinBox::valueChanged), [=](const QString &text){ /* ... */ });

错误信息是:

错误:没有匹配的函数调用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的重载。

    如果 slot 参数有未解决的重载,则需要向connect()提供第二个模板参数。 不幸的是,没有要求推断第一个的语法,因此您需要提供两者。 这就是第二种方法可以提供帮助的时候:

  • 使用正确类型的临时变量

    void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged; QObject::connect(spinBox, signal, slider, &QSlider::setValue);

    signal的赋值将选择所需的重载,现在它可以成功地替换到模板中。 这与 'slot' 参数同样有效,我发现在这种情况下它不那么麻烦。

  • 使用转换

    我们可以在这里避免使用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);

实际上,你可以用 lambda 和这个来包装你的插槽:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM