连接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) 。 您需要通过将其转换为正确的types来告诉Qt您要select哪一个:

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

我知道,这很丑 。 但是这是没有办法的。 今天的教训是: 不要超载你的信号和插槽!


附录 :演员真正讨厌的是这个

  1. 一个重复类名两次
  2. 即使通常void (对于信号),也必须指定返回值。

所以我发现自己很less使用这个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), ...) 

我个人觉得这不是很有用。 当创build者(或您的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) 

错误消息是:

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

这个重要的部分是提到“ 无法parsing的重载函数types ”。 编译器不知道你的意思是QSpinBox::valueChanged(int)还是QSpinBox::valueChanged(QString)

解决过载的方法有很多:

提供一个合适的模板参数connect()

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

这迫使connect()&QSpinBox::valueChangedparsing为需要int的重载。

如果对于slot参数有无法parsing的重载,则需要为connect()提供第二个模板参数。 不幸的是,没有语法要求首先推断,所以你需要提供。 这是第二种方法可以帮助的时候:

使用正确types的临时variables

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

signal分配将select所需的过载,现在可以将其成功replace为模板。 这与“插槽”的说法同样适用,在这种情况下,我发现它不那么麻烦。

使用转换

我们可以在这里避免使用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)>(signal), slider, &QSlider::setValue); 

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

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

会看起来更好。 :\

上面的解决scheme工作,但我用一个稍微不同的方式,使用macros来解决这个问题,所以在这里是这样的:

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