我在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
。
我做错了什么,我该如何解决?
答案 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);
我知道,这是丑陋。但是没有办法解决这个问题。今天的教训是:不要使信号和插槽过载!
附录:对演员来说真正令人讨厌的是那个
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), ...)
我个人觉得这不是很有用。我希望当Creator(或你的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)
答案 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);