如 New Signal Slot Syntax 中所述,我无法掌握 Qt 5 中新的信号/槽语法(使用指向成员函数的指针)。我试着改变这个:
QObject::connect(spinBox, SIGNAL(valueChanged(int)),
slider, SLOT(setValue(int));
对此:
QObject::connect(spinBox, &QSpinBox::valueChanged,
slider, &QSlider::setValue);
但是当我尝试编译它时出现错误:
错误:没有匹配的函数调用 QObject::connect(QSpinBox*&, <未解析的重载函数类型>, QSlider*&, void (QAbstractSlider::*)(int))
我已经在 Linux 上尝试了 clang 和 gcc,两者都使用了 -std=c++11
。
我做错了什么,我该如何解决?
这里的问题是有 两个 信号具有该名称: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);
我知道,这很丑。但是没有办法解决这个问题。今天的教训是:不要超载你的信号和插槽!
附录:演员阵容真正令人讨厌的是
一个重复类名两次一个必须指定返回值,即使它通常是无效的(对于信号)。
所以我发现自己有时会使用这个 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)
附录 3:如果您查看任何重载信号的文档,现在文档本身已经清楚地说明了重载问题的解决方案。例如,https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1 说
注意:信号 valueChanged 在此类中被重载。为了使用函数指针语法连接到这个信号,Qt 提供了一个方便的帮助器来获取函数指针,如下例所示:connect(spinBox, QOverload
错误信息是:
错误:没有匹配的函数调用 QObject::connect(QSpinBox*&, <未解析的重载函数类型>, QSlider*&, void (QAbstractSlider::*)(int))
其中重要的部分是提到了“未解决的重载函数类型”。编译器不知道您的意思是 QSpinBox::valueChanged(int)
还是 QSpinBox::valueChanged(QString)
。
有几种方法可以解决过载:
给connect()提供一个合适的模板参数 QObject::connect
使用正确类型的临时变量 void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged; QObject::connect(spinBox, 信号, 滑块, &QSlider::setValue);对信号的赋值将选择所需的重载,现在它可以成功地替换到模板中。这同样适用于“插槽”参数,我发现在这种情况下它不那么麻烦。
使用转换 我们可以在这里避免使用 static_cast,因为它只是一种强制转换,而不是去除语言的保护。我使用类似的东西: // 对于使 ?: 运算符同意的第二个和 // 第三个参数也很有用。模板
实际上,你可以用 lambda 包装你的插槽,这样:
connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
会更好看。 :\
上述解决方案有效,但我使用宏以稍微不同的方式解决了这个问题,所以以防万一:
#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);
#define CONNECTCAST(class,fun,args) static_cast<void(class::*)args>(&class::fun)
- 在这种情况下用作 CONNECTCAST(QSpinBox, valueChanged, (double))
。
不定期副业成功案例分享
static_cast
丑陋而不是旧语法,因为 the new syntax enables a compile-time check 存在旧语法在运行时会失败的信号/插槽。QSerialPort
)