连接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);
我知道,这很丑 。 但是这是没有办法的。 今天的教训是: 不要超载你的信号和插槽!
附录 :演员真正讨厌的是这个
- 一个重复类名两次
- 即使通常
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
(你也得到了qConstOverload
和qNonConstOverload
)。
用法示例(来自文档):
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::valueChanged
parsing为需要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);