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

时间:2013-05-28 14:30:47

标签: c++ qt qt5

我在Qt 5中遇到新的信号/槽语法(使用指向成员函数的指针)时遇到了麻烦,如New Signal Slot Syntax中所述。我试着改变这个:

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

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

4 个答案:

答案 0 :(得分:207)

此处的问题是,两个信号具有该名称: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(对于信号)。
  3. 所以我发现自己有时会使用这个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), ...)
    

    我个人觉得这不是很有用。我希望当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)
    

答案 1 :(得分:11)

错误消息是:

  

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

这一点的重要部分是提及&#34; 未解决的重载功能类型&#34;。编译器不知道您的意思是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的分配将选择所需的重载,现在可以成功替换为模板。这同样适用于&#39;插槽&#39;在这种情况下,我觉得它不那么麻烦。

  • 使用转化

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

答案 2 :(得分:7)

实际上,你可以用lambda包装你的插槽,并且:

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
    slider, &QSlider::setValue);

看起来会更好。 :\

答案 3 :(得分:0)

上面的解决方案有效,但是我用一种稍微不同的方式解决了这个问题,使用了一个宏,所以以下是以下情况:

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