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

dtr*_*uby 117 c++ qt qt5

我在Qt 5中无法掌握新的信号/槽语法(使用指向成员函数的指针),如新信号槽语法中所述.我尝试改变这个:

QObject::connect(spinBox, SIGNAL(valueChanged(int)),
                 slider, SLOT(setValue(int));
Run Code Online (Sandbox Code Playgroud)

对此:

QObject::connect(spinBox, &QSpinBox::valueChanged,
                 slider, &QSlider::setValue);
Run Code Online (Sandbox Code Playgroud)

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

错误:没有匹配的函数用于调用 QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

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

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

pep*_*ppe 220

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

connect(spinbox, qOverload<int>(&QSpinBox::valueChanged),
        slider, &QSlider::setValue);
Run Code Online (Sandbox Code Playgroud)

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

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
        slider, &QSlider::setValue);
Run Code Online (Sandbox Code Playgroud)

我知道,这很难看.但是没有办法解决这个问题.今天的教训是:不要使信号和插槽过载!


附录:对演员来说真正令人讨厌的是

  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;
    } 
};
Run Code Online (Sandbox Code Playgroud)

用法:

connect(spinbox, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged), ...)
Run Code Online (Sandbox Code Playgroud)

我个人认为它不是很有用.我希望当Creator(或你的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)
Run Code Online (Sandbox Code Playgroud)

  • 我对新的语法感到非常兴奋......现在冷漠的冷漠让人失望. (17认同)
  • 我个人更喜欢`static_cast`丑陋而不是旧语法,只是因为[新语法启用编译时检查](http://wiki.qt.io/New_Signal_Slot_Syntax)因为信号/槽的存在而旧语法在运行时会失败. (13认同)
  • 对于那些想知道的人(像我一样):"pmf"代表"指向成员函数的指针". (9认同)
  • 不幸的是,不重载信号通常不是一种选择——Qt 经常重载自己的信号。(例如`QSerialPort`) (2认同)

Tob*_*ght 12

错误消息是:

错误:没有匹配的函数用于调用 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);
    
    Run Code Online (Sandbox Code Playgroud)

    这迫使connect()解决&QSpinBox::valueChanged进入过载的问题int.

    如果你对slot参数有未解决的重载,那么你需要提供第二个模板参数connect().不幸的是,没有语法要求推断第一个,所以你需要提供两者.那是第二种方法可以帮助:

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

    void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged;
    QObject::connect(spinBox, signal,
                     slider,  &QSlider::setValue);
    
    Run Code Online (Sandbox Code Playgroud)

    赋值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; }
    
    Run Code Online (Sandbox Code Playgroud)

    这允许我们写

    QObject::connect(spinBox, coerce<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
                     slider, &QSlider::setValue);
    
    Run Code Online (Sandbox Code Playgroud)


New*_*fer 8

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

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
    slider, &QSlider::setValue);
Run Code Online (Sandbox Code Playgroud)

看起来会更好.:\